Est-ce une bonne pratique d'utiliser java.lang.Chaîne.stagiaire()?


Le Javadoc à propos String.intern() ne donne pas beaucoup de détails. (En un mot: Il renvoie une représentation canonique de la chaîne, permettant de comparer les chaînes internes en utilisant ==)

  • Quand j'utilise cette fonction en faveur de String.equals()?
  • Y a-t-il des effets secondaires non mentionnés dans le Javadoc, c'est-à-dire plus ou moins d'optimisation par le compilateur JIT?
  • Y a-t-il d'autres utilisations de String.intern()?
Author: Daniel Rikowski, 2009-07-07

20 answers

Quand utiliserais-je cette fonction en faveur de la chaîne.equals ()

Lorsque vous avez besoin de speed puisque vous pouvez comparer des chaînes par référence (==est plus rapide que equals)

Y a-t-il des effets secondaires non mentionnés dans le Javadoc?

Le principal inconvénient est que vous devez vous rappeler pour vous assurer que vous faites réellement stagiaire() toutes les chaînes que vous allez comparer. Il est facile d'oublier de intern () toutes les chaînes et vous pouvez obtenir résultats confusément incorrects. De plus, pour le bien de tous, assurez-vous de documenter très clairement que vous comptez sur les chaînes en cours d'internalisation.

Le deuxième inconvénient si vous décidez d'internaliser des chaînes est que la méthode intern() est relativement coûteuse. Il doit gérer le pool de chaînes uniques afin de faire un peu de travail (même si la chaîne a déjà été internalisée). Soyez donc prudent dans la conception de votre code afin que, par exemple, intern () toutes les chaînes appropriées en entrée donc, vous n'avez plus à vous en soucier.

(de JGuru)

Troisième inconvénient (Java 7 ou moins uniquement): les chaînes internées vivent dans l'espace PermGen, qui est généralement assez petit; vous pouvez rencontrer une OutOfMemoryError avec beaucoup d'espace de tas libre.

(de Michael Borgwardt)

 121
Author: dfa, 2015-01-02 09:05:24

Cela n'a (presque) rien à voir avec la comparaison de chaînes. String interning est destiné à enregistrer de la mémoire si vous avez plusieurs chaînes avec le même contenu dans votre application. En utilisant String.intern(), l'application n'aura qu'une seule instance à long terme et un effet secondaire est que vous pouvez effectuer une comparaison rapide de l'égalité de référence au lieu d'une comparaison de chaîne ordinaire (mais ce n'est généralement pas conseillé car il est vraiment facile de casser en oubliant de n'interner qu'un seul instance).

 188
Author: Daniel Brückner, 2009-07-07 08:45:05

String.intern() est certainement les ordures collectées dans les JVM modernes.
Ce qui suit NE manque JAMAIS de mémoire, en raison de l'activité du GC:

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

Voir plus (de moi) sur le mythe de la chaîne non GCed.stagiaire().

 36
Author: Gili Nachum, 2014-04-28 18:22:01

J'ai récemment écrit un article sur String.implémentation de intern () dans Java 6, 7 et 8: Chaîne.stagiaire en Java 6, 7 et 8-string pooling .

J'espère qu'il devrait contenir suffisamment d'informations sur la situation actuelle avec le regroupement de chaînes en Java.

En un mot:

  • Évitez String.intern() dans Java 6, car il va dans PermGen
  • Préférez String.intern() en Java 7 et Java 8: il utilise 4 à 5 fois moins de mémoire que de rouler votre propre pool d'objets
  • assurez-vous de syntoniser -XX:StringTableSize (la valeur par défaut est probablement trop petite; définir un nombre premier)
 15
Author: mik1, 2013-11-02 16:09:54

Comparer des chaînes avec == est beaucoup plus rapide qu'avec equals()

5 Temps plus rapide, mais comme la comparaison de chaînes ne représente généralement qu'un faible pourcentage du temps d'exécution total d'une application, le gain global est beaucoup plus petit que cela, et le gain final sera dilué à quelques pour cent.

Chaîne de caractères.intern() éloigne la chaîne du tas et la place dans PermGen

Les chaînes internalisées sont placées dans une zone de stockage différente : Génération permanente qui est une zone de la JVM réservée aux objets non-utilisateur, comme les Classes, les Méthodes et autres objets JVM internes. La taille de cette zone est limitée et est beaucoup plus précieux que le tas. Étant cette zone plus petite que le tas, il y a plus de probabilité d'utiliser tout l'espace et d'obtenir une OutOfMemoryException.

Chaîne de caractères.la chaîne intern() est récupérée

Dans les nouvelles versions de JVM les chaînes internalisées sont également collectées lorsqu'il n'est référencé par aucun objet.

En gardant à l'esprit les 3 points ci-dessus, vous pouvez déduire que String intern() ne pourrait être utile que dans quelques situations lorsque vous faites beaucoup de comparaison de chaînes, mais il est préférable de ne pas utiliser de chaîne interne si vous ne savez pas exactement ce que vous faites ...

 13
Author: aleroot, 2011-09-24 10:00:40

Je ne connais aucun avantage, et s'il y en avait un, on pourrait penser que equals() utiliserait lui-même intern() en interne (ce qui n'est pas le cas).

Faire tomber les mythes intern ()

 8
Author: objects, 2009-07-07 08:41:55

Quand utiliserais-je cette fonction en faveur de la chaîne.equals ()

Étant donné qu'ils font des choses différentes, probablement jamais.

Interner des chaînes pour des raisons de performances afin que vous puissiez les comparer pour l'égalité de référence ne sera bénéfique que si vous détenez des références aux chaînes pendant un certain temps - les chaînes provenant de l'entrée utilisateur ou des E / S ne seront pas internées.

Cela signifie que dans votre application, vous recevez une entrée d'une source externe et la traitez dans un objet qui a une valeur sémantique-un identifiant par exemple-mais cet objet a un type indiscernable des données brutes, et a des règles différentes quant à la façon dont le programmeur doit l'utiliser.

Il est presque toujours préférable de créer un type UserId qui est interné ( il est facile de créer un mécanisme d'internement générique sans thread ) et agit comme une énumération ouverte, que de surcharger le type java.lang.String avec une sémantique de référence s'il s'agit d'un ID utilisateur.

De cette façon, vous n'obtenez pas de confusion entre qu'une chaîne particulière ait été internée ou non, et vous pouvez encapsuler tout comportement supplémentaire dont vous avez besoin dans l'énumération ouverte.

 6
Author: Pete Kirkham, 2009-07-07 08:58:18

Y a-t-il des effets secondaires non mentionnés dans le Javadoc, c'est-à-dire plus ou moins d'optimisation par le compilateur JIT?

Je ne connais pas le niveau JIT, mais il existe un support direct du bytecode pour le pool de chaînes, qui est implémenté comme par magie et efficacement avec une structure CONSTANT_String_info dédiée (contrairement à la plupart des autres objets qui ont des représentations plus génériques).

JVM

JVM 7 5.1, dit - :

Un littéral de chaîne est un référence à une instance de chaîne de classe, et est dérivée d'une structure CONSTANT_String_info (§4.4.3) dans la représentation binaire d'une classe ou d'une interface. La structure CONSTANT_String_info donne la séquence des points de code Unicode constituant le littéral de chaîne.

Le langage de programmation Java exige que les littéraux de chaîne identiques (c'est-à-dire les littéraux contenant la même séquence de points de code) se réfèrent à la même instance de chaîne de classe (JLS §3.10.5). Outre, si la chaîne de méthode.intern est appelé sur n'importe quelle chaîne, le résultat est une référence à la même instance de classe qui serait renvoyée si cette chaîne apparaissait en tant que littéral. Ainsi, l'expression suivante doit avoir la valeur true:

("a" + "b" + "c").intern() == "abc"

Pour dériver un littéral de chaîne, la machine virtuelle Java examine la séquence de points de code donnée par la structure CONSTANT_String_info.

  • Si la chaîne de méthode.stagiaire a déjà été appelé sur une instance de chaîne de classe contenant une séquence de points de code Unicode identique à celle donnée par la structure CONSTANT_String_info, alors le résultat de la dérivation littérale de chaîne est une référence à cette même instance de chaîne de classe.

  • Sinon, une nouvelle instance de chaîne de classe est créée contenant la séquence de points de code Unicode donnés par la structure CONSTANT_String_info; une référence à cette instance de classe est le résultat de la dérivation littérale de chaîne. Enfin, la méthode stagiaire de la nouvelle instance de Chaîne est invoquée.

Bytecode

Il est également instructif de regarder l'implémentation du bytecode sur OpenJDK 7.

Si on décompile:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

Nous avons sur le pool constant:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

Et main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Notez comment:

  • 0 et 3: la même constante ldc #2 est chargée (les littéraux)
  • 12: une nouvelle instance de chaîne est créé (avec #2 comme argument)
  • 35: a et {[13] } sont comparés en tant qu'objets réguliers avec if_acmpne

La représentation des chaînes constantes est assez magique sur le bytecode:

  • il a une structure dédiée CONSTANT_String_info, contrairement aux objets réguliers (par exemple new String)
  • la structure pointe vers une structure CONSTANT_Utf8_info qui contient les données. C'est les seules données nécessaires pour représenter la chaîne.

Et la citation de JVM ci-dessus semble dire que chaque fois que l'Utf8 pointé est le même, alors des instances identiques sont chargées par ldc.

J'ai fait des tests similaires pour les champs, et:

  • static final String s = "abc" pointe vers la table constante via l'attribut ConstantValue
  • les champs non finaux n'ont pas cet attribut, mais peuvent toujours être initialisés avec ldc

Bonus : comparez cela au Pool entier , qui n'a pas de bytecode direct support (c'est-à-dire pas CONSTANT_String_info analogique).

J'examinerais intern et ==-comparison au lieu d'equals uniquement dans le cas de equals-comparison étant un goulot d'étranglement dans plusieurs comparaisons de chaînes. Il est très peu probable que cela aide avec un petit nombre de comparaisons, car intern() n'est pas gratuit. Après avoir interné agressivement des chaînes, vous trouverez que les appels à intern () deviennent de plus en plus lents.

 2
Author: Mikko Maunu, 2009-07-07 08:46:27

Une sorte de fuite de mémoire peut venir de l'utilisation de subString() lorsque le résultat est petite par rapport à la chaîne source et l'objet a une longue durée de vie.

La solution normale consiste à utiliser new String( s.subString(...)) mais lorsque vous avez une classe qui stocke le résultat d'un subString(...) potentiel/probable et n'a aucun contrôle sur l'appelant, vous pouvez envisager de stocker le intern() des arguments de chaîne transmis au constructeur. Cela libère le grand tampon potentiel.

 2
Author: eremmel, 2012-07-16 13:42:43

Daniel Brückner a tout à fait raison. String interning est destiné à économiser de la mémoire (tas). Notre système a actuellement un hashmap géant pour contenir certaines données. Au fur et à mesure que le système évolue, le hashmap sera suffisamment grand pour que le tas manque de mémoire (comme nous l'avons testé). En internant toutes les chaînes dupliquées tous les objets dans le hashmap, cela nous permet d'économiser une quantité importante d'espace de tas.

Toujours dans Java 7, les chaînes internées ne vivent plus longtemps dans PermGen mais dans le tas à la place. Donc vous ne besoin de s'inquiéter de sa taille et oui, il reçoit des ordures collectées:

Dans JDK 7, les chaînes internées ne sont plus allouées dans le permanent génération du tas Java, mais sont plutôt alloués dans le principal une partie du tas de Java (connu sous le nom de jeunes et vieilles générations), le long avec les autres objets créés par l'application. Ce changement résultat: plus de données résident dans le tas Java principal et moins de données dans la génération permanente, et peut donc nécessiter des tailles de tas pour être ajuster. La plupart des applications ne verront que des différences relativement faibles dans l'utilisation du tas en raison de ce changement, mais les applications plus grandes qui se chargent de nombreuses classes ou font un usage intensif de la chaîne.stagiaire() la méthode verrez des différences plus significatives.

 2
Author: xli, 2013-04-26 00:28:10

String interning est utile dans le cas où la méthode equals() est souvent invoquée car la méthode equals() vérifie rapidement si les objets sont les mêmes au début de la méthode.

if (this == anObject) {
    return true;
}

Cela se produit généralement lors de la recherche dans un Collection bien qu'un autre code puisse également effectuer des vérifications d'égalité de chaîne.

Il y a un coût à l'internement cependant, j'ai effectué un microbenchmark de code et j'ai constaté que le processus d'internement augmente le temps d'exécution d'un facteur 10.

Le meilleur endroit pour effectuer le stage est généralement lorsque vous lisez des clés qui sont stockées en dehors du code car les chaînes du code sont automatiquement internées. Cela se produirait normalement aux étapes d'initialisation de votre application afin d'éviter la pénalité du premier utilisateur.

Un autre endroit où cela peut être fait est lors du traitement des entrées utilisateur qui pourraient être utilisées pour effectuer des recherches de clés. Cela se produit normalement dans votre processeur de requête, notez que les chaînes internées doit être transmis.

En dehors de cela, il n'y a pas beaucoup d'intérêt à faire un stage dans le reste du code car cela ne donnera généralement aucun avantage.

 2
Author: Archimedes Trajano, 2014-08-03 16:30:34

Je voterais pour cela ne vaut pas les tracas d'entretien.

La plupart du temps, il n'y aura pas besoin, et aucun avantage de performance, sauf si votre code fait beaucoup de travail avec des sous-chaînes. Auquel cas, la classe String utilisera la chaîne d'origine, plus un décalage pour économiser de la mémoire. Si votre code utilise beaucoup de sous-chaînes, je soupçonne que cela va simplement faire exploser vos besoins en mémoire.

 1
Author: wm_eddie, 2009-07-07 09:27:54

Http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

Affirme que String.equals() utilise "==" pour comparer String objets avant, selon

Http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html

Il compare les longueurs des chaînes, puis le contenu.

(Soit dit en passant, les chaînes de code de produit dans un catalogue de vente sont susceptibles d'avoir toutes la même longueur-BIC0417 est le bicyciste casque de sécurité, TIG0003 est un tigre mâle adulte vivant - vous avez probablement besoin de toutes sortes de licences pour en commander une. Et peut-être que vous feriez mieux de commander un casque de sécurité en même temps.)

Il semble donc que vous obteniez un avantage en remplaçant vos chaînes par leur version intern(), mais vous obtenez la sécurité - et la lisibilité et la conformité standard - -sans - utiliser "==" pour equals() dans votre programmation. Et la plupart de ce que je vais dire dépend que d'être vrai, si c'est vrai.

Mais est-ce que String.equals() teste que vous lui avez passé une chaîne et pas un autre objet, avant d'utiliser "==" ? Je ne suis pas qualifié pour le dire, mais je suppose que non, car la plupart de ces opérations equals() seront Chaîne à chaîne, de sorte que le test est presque toujours réussi. En effet, donner la priorité à "= = " dans String.equals() implique une confiance que vous comparez fréquemment la Chaîne au même objet réel.

J'espère que personne n'est surpris que les lignes suivantes produisent un résultat de "faux":

    Integer i = 1;
    System.out.println("1".equals(i));

, Mais si vous changez i à i.toString(), dans la deuxième ligne, bien sûr, c'est true.

Lieux où vous pourriez espérer un avantage de stage comprennent Set et Map, évidemment. J'espère que les chaînes internées ont leurs hashcodes mis en cache... Je pense que ce serait une exigence. Et j'espère que je n'ai pas donné une idée qui pourrait me rapporter un million de dollars. :-)

En ce qui concerne la mémoire, il est également évident que c'est une limite importante si votre volume de Strings est grand, ou si vous voulez que la mémoire utilisée par votre code de programme soit très petite. Si votre volume de chaînes distinctes est très important, il est peut-être temps d'envisager d'utiliser un code de programme de base de données dédié pour les gérer et un serveur de base de données séparé. De même, si vous pouvez améliorer un petit programme (qui doit s'exécuter simultanément dans 10000 instances) en ne stockant pas du tout ses chaînes.

Il semble inutile de créer une nouvelle chaîne, puis de la jeter immédiatement pour son intern() substitut, mais il n'y a pas d'alternative claire, sauf pour garder la chaîne en double. Donc, le coût d'exécution consiste à rechercher votre chaîne dans le pool interne, puis à permettre au garbage collector de disposer de l'original. Et si c'est un littéral de chaîne, il vient déjà de toute façon.

Je me demande si intern() peut être abusé par un code de programme malveillant pour détecter si certaines chaînes et leurs références d'objet existent déjà dans le pool intern() , et existent donc ailleurs dans la session Java, alors que cela ne devrait pas être connu. Mais cela ne serait possible que lorsque le code du programme est déjà utilisé de manière confiante, je suppose. Pourtant, il est quelque chose à considérer à propos des bibliothèques tierces que vous incluez dans votre programme pour stocker et mémoriser vos numéros de code PIN ATM!

 1
Author: Robert Carnegie, 2012-10-27 07:14:32

La vraie raison d'utiliser intern n'est pas ce qui précède. Vous pouvez l'utiliser après avoir obtenu une erreur de mémoire insuffisante. Beaucoup de chaînes dans un programme typique sont des chaînes.substring () d'une autre grande chaîne [pensez à retirer un nom d'utilisateur d'un fichier XML 100K. L'implémentation java est que, la sous-chaîne contient une référence à la chaîne d'origine et le début+fin dans cette énorme chaîne. (La pensée derrière c'est une réutilisation de la même grosse chaîne)

Après 1000 gros fichiers, dont vous n'enregistrez que 1000 noms courts, vous garderez en mémoire l'ensemble des 1000 fichiers! Solution: dans ce scénario, utilisez simplement smallsubstring.stagiaire()

 0
Author: asaf, 2010-07-22 20:25:04

J'utilise intern pour économiser de la mémoire, je tiens une grande quantité de données de chaîne en mémoire et le fait d'utiliser intern() a économisé une quantité massive de mémoire. Malheureusement, bien qu'il utilise beaucoup moins de mémoire que la mémoire n'est conservée dans le PermGen la mémoire de Tas et il est difficile d'expliquer aux clients comment augmenter l'allocation de ce type de mémoire.

Existe-t-il donc une alternative à intern () pour réduire la consommation de mémoire, (les avantages de performance == versus equals ne sont pas un problème pour moi)

 0
Author: Paul Taylor, 2010-09-09 13:27:06

Avouons-le: le scénario d'utilisation principal est lorsque vous lisez un flux de données (soit via un flux d'entrée, soit à partir d'un jeu de résultats JDBC) et qu'il y a une myriade de petites chaînes qui sont répétées tout au long.

Voici une petite astuce qui vous donne un certain contrôle sur le type de mécanisme que vous souhaitez utiliser pour internaliser les chaînes et autres immutables, et un exemple d'implémentation:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

Je l'utilise souvent lorsque je lis des champs de flux ou de ResultSets. Remarque: LRUCache est un cache simple basé sur LinkedHashMap<K,V>. Il appelle automatiquement la méthode retrieve() fournie par l'utilisateur pour tous les échecs de cache.

La façon de l'utiliser est d'en créer un LRUInternalizer avant votre lecture (ou vos lectures), de l'utiliser pour internaliser des chaînes et d'autres petits objets immuables, puis de le libérer. Par exemple:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}
 0
Author: Pierre D, 2012-08-14 21:53:58

Je l'utilise afin de mettre en cache le contenu d'environ 36000 codes qui renvoient aux noms associés. Je stagiaire les chaînes dans le cache car de nombreux codes pointent vers la même chaîne.

En internant les chaînes dans mon cache, je m'assure que les codes qui pointent vers la même chaîne pointent réellement vers la même mémoire, me permettant ainsi d'économiser de l'espace RAM.

Si les chaînes internées étaient en fait des ordures collectées, cela ne fonctionnerait pas du tout pour moi. Ce serait essentiellement nier le but du stage. Le mien ne sera pas collecté car je tiens une référence à chaque chaîne dans le cache.

 0
Author: Rodney P. Barbati, 2013-08-16 00:20:07

Le coût de l'internement d'une chaîne est beaucoup plus que le temps économisé dans une seule chaîne.égale (B) comparaison. Utilisez-le uniquement (pour des raisons de performances) lorsque vous utilisez à plusieurs reprises les mêmes variables de chaîne inchangées. Par exemple, si vous parcourez régulièrement une liste stable de chaînes pour mettre à jour certaines cartes saisies sur le même champ de chaîne, vous pouvez obtenir une belle sauvegarde.

Je suggère d'utiliser string interning pour modifier les performances lorsque vous optimisez des parties spécifiques de votre code.

Rappelez-vous également que les chaînes sont immuables et ne faites pas l'erreur stupide de

String a = SOME_RANDOM_VALUE
a.intern()

N'oubliez pas de faire

String a = SOME_RANDOM_VALUE.intern()
 0
Author: grumblebee, 2013-09-16 06:57:33

Si vous recherchez un remplacement illimité pour String.stagiaire, également les ordures collectées, ce qui suit fonctionne bien pour moi.

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

Bien sûr, si vous pouvez estimer à peu près combien de chaînes différentes il y aura, utilisez simplement String.il est possible de créer un fichier avec une valeur de type-XX:StringTableSize=highEnoughValue.

 0
Author: bdruemen, 2016-11-04 14:22:16