Est-ce une bonne pratique d'utiliser java.lang.String.intern ()?

194

Le Javadoc sur String.intern()ne donne pas beaucoup de détails. (En résumé: il renvoie une représentation canonique de la chaîne, permettant de comparer les chaînes internées à l'aide ==)

  • Quand devrais-je utiliser 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 String.intern()?
Daniel Rikowski
la source
14
L'appel de stagiaire () a son propre impact sur la performance, l'utilisation de stagiaire () pour améliorer les performances doit être testée afin de s'assurer qu'elle accélère vraiment votre programme de manière significative pour valoir la complexité supplémentaire. Vous pouvez également l'utiliser pour réduire la consommation de mémoire pour les grandes tables avec des valeurs répétitives fiables. Cependant, dans les deux cas, il existe d'autres options qui pourraient être meilleures.
Peter Lawrey du
Oui, intern () a son propre impact sur les performances. Surtout parce que le coût intern () augmente linéairement lorsque vous internez des chaînes et conservez une référence à celles-ci. Au moins sur un soleil / oracle 1.6.0_30 vm.
lacroix1547

Réponses:

125

Quand utiliserais-je cette fonction en faveur de String.equals ()

lorsque vous avez besoin de vitesse, car vous pouvez comparer les chaînes par référence (== est plus rapide qu'égal)

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

Le principal inconvénient est que vous devez vous rappeler de vous assurer que vous effectuez bien intern () toutes les chaînes que vous allez comparer. Il est facile d'oublier d'interner () toutes les chaînes et vous pouvez alors obtenir des résultats confus et incorrects. Aussi, 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 bon travail (même si la chaîne a déjà été internalisée). Donc, soyez prudent dans la conception de votre code pour que vous, par exemple, intern () toutes les chaînes appropriées en entrée afin de ne plus avoir à vous en soucier.

(de JGuru)

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

(de Michael Borgwardt)

dfa
la source
64
Un troisième inconvénient: les cordes internées vivent dans l'espace PermGen, qui est généralement assez petit; vous pouvez rencontrer une OutOfMemoryError avec beaucoup d'espace libre.
Michael Borgwardt
15
Les nouvelles machines virtuelles AFAIK récupèrent également l'espace PermGen.
Daniel Rikowski
31
Stagiaire concerne la gestion de la mémoire, pas la vitesse de comparaison. La différence entre if (s1.equals(s2))et if (i1 == i2)est minime, sauf si vous avez beaucoup de longues chaînes avec les mêmes caractères principaux. Dans la plupart des utilisations réelles (autres que les URL), les chaînes diffèrent au sein des premiers caractères. Et les longues chaînes if-else sont de toute façon une odeur de code: utilisez des énumérations et des cartes de foncteurs.
kdgregory
25
vous pouvez toujours utiliser la syntaxe s1.equals dans tout votre programme, N'UTILISEZ PAS ==, .equals utilisez == en interne pour court-circuiter l'évaluation
gtrak
15
Michael Borgwardt n'a PAS dit que les chaînes internes ne peuvent pas être récupérées. Et c'est une affirmation FAUX. Ce que les commentaires de Michael disent (correctement) est plus subtil que cela.
Stephen C
193

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

Daniel Brückner
la source
4
Ce n'est pas juste. L'internement de chaînes se produit toujours, automatiquement, lorsque chaque expression de chaîne est évaluée. Il y a toujours une copie pour chaque chaîne de caractères unique utilisée et elle est "partagée en interne" si plusieurs utilisations se produisent. L'appel de String.intern () ne fait pas tout cela se produire - il renvoie simplement la représentation canonique interne. Voir javadoc.
Glen Best
16
Besoin de clarifier - l'internement se produit toujours automatiquement pour les chaînes constantes au moment de la compilation (littéraux et expressions fixes). En outre, il se produit lorsque String.intern () est appelé sur des chaînes évaluées dynamiquement à l'exécution.
Glen Best
Donc vous voulez dire, s'il y a 1000 objets de "Bonjour" dans Heap et que j'exécute intern () sur l'un d'eux, alors 999 objets restants seront détruits automatiquement?
Arun Raaj
@ArunRaaj non, vous aurez votre 1000 toujours sur le tas, et un supplémentaire dans la piscine de stagiaire, qui peut être prêt pour la réutilisation par la suite str.intern()quand strest "Hello".
Matthieu
37

String.intern()est définitivement récupéré dans les JVM modernes.
La mémoire suivante ne manque JAMAIS, 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 non GCed String.intern () .

Gili Nachum
la source
26
OutOfMemoryException- non, pas le code ci-dessus, dans mon cerveau : lien vers l'article javaturning, qui pointe vers cet article, qui pointe vers l'article javaturning, qui ... :-)
user85421
Bien que vous puissiez voir que le message a été modifié pour ajouter ce lien;)
Riking
3
Vous voudrez peut-être mentionner que vous êtes également l'auteur de la référence externe à laquelle vous liez.
Thorbjørn Ravn Andersen
11
@Carlos liant une référence externe qui renvoie vers stackoverflow devrait provoquer un .. Stackoverflow :)
Seiti
2
@Seiti Les références circulaires sont facilement détectées ces jours-ci: p
Ajay
16

J'ai récemment écrit un article sur l'implémentation de String.intern () dans Java 6, 7 et 8: String.intern dans Java 6, 7 et 8 - regroupement de chaînes .

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

En un mot:

  • A éviter String.intern()en Java 6, car il va dans PermGen
  • Préférez String.intern()dans Java 7 et Java 8: il utilise 4 à 5 fois moins de mémoire que le roulement de votre propre pool d'objets
  • Assurez-vous de régler -XX:StringTableSize(la valeur par défaut est probablement trop petite; définissez un nombre premier)
mik1
la source
3
S'il vous plaît ne postez pas seulement des liens vers votre blog, cela est considéré par certains comme du SPAM. De plus, les liens de blog ont une tendance notable à mourir en 404. Veuillez résumer votre article en ligne ici, ou laisser ce lien dans un commentaire à la question.
Mat
3
Merci d'avoir écrit ça @ mik1! Article très informatif, clair et à jour. (Je suis revenu ici avec l'intention de publier un lien vers celui-ci.)
Luke Usherwood
1
Merci d'avoir mentionné l'argument -XX. Vous pouvez également l'utiliser pour voir les statistiques du tableau: -XX: + PrintStringTableStatistics
csadler
13

La comparaison de 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.

String.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: la 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 d'autres objets JVM internes. La taille de cette zone est limitée et est beaucoup plus précieuse que le tas. Cette zone étant plus petite que Heap, il y a plus de chances d'utiliser tout l'espace et d'obtenir une OutOfMemoryException.

Les chaînes String.intern () sont récupérées

Dans les nouvelles versions de JVM, les chaînes internalisées sont également récupérées lorsqu'elles ne sont référencées par aucun objet.

En gardant à l'esprit les 3 points ci-dessus, vous pouvez déduire que String intern () ne peut ê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 ...

aleroot
la source
1
Juste pour ajouter, les exceptions de mémoire Heap peuvent parfois être récupérées, en particulier dans les modèles filetés tels que les applications Web. Lorsque permgen est épuisé, une application sera généralement non fonctionnelle de manière permanente et souvent, la ressource sera détruite jusqu'à sa mort.
Taylor
7

Quand utiliserais-je cette fonction en faveur de String.equals ()

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

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

Cela signifie que dans votre application, vous recevez des entrées d'une source externe et les traitez en un objet qui a une valeur sémantique - un identificateur, par exemple - mais cet objet a un type qui ne se distingue pas des données brutes et a des règles différentes sur la façon dont le programmeur doit utilise le.

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

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

Pete Kirkham
la source
6

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

Briser les mythes internes ()

objets
la source
7
Bien que vous disiez que vous n'êtes au courant d'aucun avantage, votre lien publié identifie la comparaison via == comme étant 5 fois plus rapide et donc importante pour le code performant centré sur le texte
Brian Agnew
3
Lorsque vous avez beaucoup de comparaison de texte à faire, vous finirez par manquer d'espace PermGen. Lorsqu'il n'y a pas tellement de comparaison de texte à faire, la différence de vitesse n'a pas d'importance. Dans tous les cas, n'internez pas () vos chaînes. Ça ne vaut pas le coup.
Bombe
Il ajoute également que le gain relatif global sera généralement faible.
objets
Je ne pense pas que ce genre de logique soit valide. Bon lien cependant!
Daniel Rikowski
1
@DR: quelle logique? C'est une grande erreur. @objects: désolé, mais vos arguments sont en deçà des raisons. Il y a de très bonnes raisons d'utiliser internet de très bonnes raisons qui equalsne le font pas par défaut. Le lien que vous avez publié est complet. Le dernier paragraphe admet même qu'il interna un scénario d'utilisation valide: un traitement de texte lourd (par exemple un analyseur). Conclure que «[XYZ] est dangereux si vous ne savez pas ce que vous faites» est si banal que ça fait mal physiquement.
Konrad Rudolph
4

Daniel Brückner a absolument raison. L'internement de chaînes est destiné à économiser de la mémoire (tas). Notre système possède actuellement une table de hachage géante pour conserver certaines données. À mesure que le système évolue, la table de hachage sera suffisamment grande pour libérer de la mémoire (comme nous l'avons testé). En internant toutes les chaînes dupliquées tous les objets dans la table de hachage, il nous permet d'économiser une quantité importante d'espace de tas.

Également en Java 7, les chaînes internées ne vivent plus dans PermGen mais plutôt en tas. Vous n'avez donc pas à vous soucier de sa taille et oui, il est récupéré:

Dans JDK 7, les chaînes internes ne sont plus allouées dans la génération permanente du tas Java, mais sont plutôt allouées dans la partie principale du tas Java (appelées les générations jeunes et anciennes), ainsi que les autres objets créés par l'application. . Cette modification entraînera plus de données résidant 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 à ajuster. La plupart des applications ne verront que des différences relativement faibles dans l'utilisation du segment de mémoire en raison de ce changement, mais les applications plus grandes qui chargent de nombreuses classes ou utilisent fortement la méthode String.intern () verront des différences plus importantes.

xli
la source
Je dois appuyer cela: sur mon logiciel, un vidage de tas a montré que la plupart de l'espace de tas était utilisé par les Stringinstances. En regardant leur contenu, j'ai vu de nombreux doublons et j'ai décidé de passer à intern(), ce qui a permis d'économiser des centaines de Mo.
Matthieu
4

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 y a un support direct de bytecode pour le pool de chaînes , qui est implémenté comme par magie et efficacement avec une CONSTANT_String_infostructure dédiée (contrairement à la plupart des autres objets qui ont des représentations plus génériques).

JVMS

JVMS 7 5.1 dit :

Un littéral de chaîne est une référence à une instance de la classe String et est dérivé 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 de points de code Unicode constituant le littéral chaîne.

Le langage de programmation Java requiert que des littéraux de chaîne identiques (c'est-à-dire des littéraux qui contiennent la même séquence de points de code) doivent faire référence à la même instance de classe String (JLS §3.10.5). En outre, si la méthode String.intern est appelée sur une 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 comme un 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 méthode String.intern a déjà été appelée sur une instance de classe String contenant une séquence de points de code Unicode identique à celle donnée par la structure CONSTANT_String_info, le résultat de la dérivation littérale de chaîne est une référence à cette même instance de classe String.

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

Bytecode

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

Si nous décompilons:

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 bassin 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:

  • 0et 3: la même ldc #2constante est chargée (les littéraux)
  • 12: une nouvelle instance de chaîne est créée (avec #2comme argument)
  • 35: aet csont comparés comme des objets normaux avecif_acmpne

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

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

et la citation JVMS ci-dessus semble dire que chaque fois que l'Utf8 pointé est le même, 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 des constantes 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 Integer , qui n'a pas de prise en charge directe du bytecode (c'est-à-dire pas d' CONSTANT_String_infoanalogue).

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
2

J'examinerais la comparaison interne et == - au lieu de l'égalité uniquement dans le cas où la comparaison égale est 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é les chaînes de manière agressive, vous constaterez que les appels à intern () deviennent de plus en plus lents.

Mikko Maunu
la source
2

Une sorte de fuite de mémoire peut provenir de l'utilisation de subString()lorsque le résultat est petit par rapport à la chaîne source et que 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 potentiel / probable subString(...)et que vous n'avez aucun contrôle sur l'appelant, vous pouvez envisager de stocker les intern()arguments String passés au constructeur. Cela libère le grand tampon potentiel.

eremmel
la source
Intéressant, mais cela dépend peut-être de la mise en œuvre.
akostadinov
1
La fuite de mémoire potentielle mentionnée ci-dessus ne se produit pas dans java 1.8 et 1.7.06 (et versions ultérieures), voir Modifications de la représentation interne de chaîne effectuées dans Java 1.7.0_06 .
eremmel
qui confirme que les micro-optimisations ne doivent être appliquées que lorsque cela est nécessaire après un profilage des performances et / ou de la mémoire. Je vous remercie.
akostadinov
2

L'internement de chaînes est utile dans le cas où la equals()méthode est souvent invoquée car la equals()méthode 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 Collectioncode bien qu'un autre code puisse également effectuer des vérifications d'égalité de chaîne.

Cependant, il y a un coût lié à l'internement.J'ai effectué une analyse comparative d'un code et j'ai constaté que le processus d'internement augmentait le temps d'exécution d'un facteur 10.

Le meilleur endroit pour effectuer l'internement est généralement lorsque vous lisez des clés stockées en dehors du code car les chaînes du code sont automatiquement internées. Cela se produit 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 clés. Cela se produit normalement dans votre processeur de requêtes, notez que les chaînes internes doivent être transmises.

En dehors de cela, il est inutile de faire un internat dans le reste du code car cela ne donnera généralement aucun avantage.

Archimedes Trajano
la source
1

Je voterais pour que cela ne vaille pas la peine d'être entretenu.

La plupart du temps, il n'y aura aucun besoin et aucun avantage en termes de performances, à moins que votre code ne travaille beaucoup avec des sous-chaînes. Dans ce 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 entraînera simplement l'explosion de vos besoins en mémoire.

wm_eddie
la source
1

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

affirme que String.equals()utilise "=="pour comparer des Stringobjets avant, selon

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

il compare les longueurs de chaînes, puis le contenu.

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

Il semble donc que vous ayez un avantage à remplacer vos cordes par leur intern()version, mais vous obtenez la sécurité - et la lisibilité et la conformité standard - -sans- en utilisant "==" pour equals()dans votre programmation. Et la plupart de ce que je vais dire dépend de ce qui est vrai, si c'est vrai.

Mais String.equals()teste-t-on que vous lui avez passé une chaîne et non un autre objet, avant de l'utiliser "=="? Je ne suis pas qualifié pour le dire, mais je suppose que non, car la plupart des equals()opérations de ce type seront de chaîne en chaîne, de sorte que le test est presque toujours réussi. En effet, prioriser "==" à l'intérieur 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 "faux":

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

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

Les lieux où vous pourriez espérer bénéficier d'un stage incluent Setet Map, évidemment. J'espère que les chaînes internes ont leurs codes de hachage mis en cache ... Je pense que ce serait une exigence. Et j'espère que je n'ai pas simplement 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 chaînes est important, 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 -distinct- est très important, il 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 distinct. De même, si vous pouvez améliorer un petit programme (qui doit s'exécuter dans 10000 instances simultanément) en ne lui 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 conserver la chaîne en double. Donc, vraiment, le coût d'exécution consiste à rechercher votre chaîne dans le pool interne puis à permettre au garbage collector de se débarrasser de l'original. Et si c'est un littéral de chaîne, il est déjà interné de toute façon.

Je me demande si intern()un code de programme malveillant peut abuser pour détecter si certaines chaînes et leurs références d'objet existent déjà dans le intern()pool, et donc existent 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 fiable, je suppose. Pourtant, c'est quelque chose à considérer sur les bibliothèques tierces que vous incluez dans votre programme pour stocker et mémoriser vos numéros PIN ATM!

Robert Carnegie
la source
0

La vraie raison d'utiliser stagiaire n'est pas la ci-dessus. Vous pouvez l'utiliser après une erreur de mémoire insuffisante. Beaucoup de chaînes dans un programme typique sont String.substring () d'une autre grande chaîne [pensez à retirer un nom d'utilisateur d'un fichier xml 100K. L'implémentation Java est la suivante: la sous-chaîne contient une référence à la chaîne d'origine et le début + la 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, à partir desquels vous n'enregistrez que 1000 noms courts, vous garderez en mémoire les 1000 fichiers entiers! Solution: dans ce scénario, utilisez simplement smallsubstring.intern ()

asaf
la source
Pourquoi ne pas simplement créer une nouvelle chaîne à partir de la sous-chaîne si vous en avez besoin?
Thorbjørn Ravn Andersen
0

J'utilise stagiaire pour économiser de la mémoire, je conserve une grande quantité de données String en mémoire et passer à utiliser stagiaire () a économisé une énorme quantité de mémoire. Malheureusement, bien qu'il utilise beaucoup moins de mémoire, la mémoire qu'il utilise est stockée dans la mémoire PermGen et non pas Heap et il est difficile d'expliquer aux clients comment augmenter l'allocation de ce type de mémoire.

Existe-t-il 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)

Paul Taylor
la source
0

Avouons-le: le scénario de cas d'utilisation principal est lorsque vous lisez un flux de données (soit via un flux d'entrée, soit depuis un jeu de résultats JDBC) et qu'il existe 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();
    }
}

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

La façon de l'utiliser consiste à en créer un LRUInternalizeravant votre lecture (ou lectures), à l'utiliser pour internaliser les chaînes et autres petits objets immuables, puis à les 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...
}
Pierre D
la source
0

Je l'utilise afin de mettre en cache le contenu d'environ 36 000 codes liés à des noms associés. J'interne 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 ramassées, cela ne marcherait pas du tout pour moi. Cela annulerait fondamentalement l'objectif du stage. Le mien ne sera pas récupéré parce que je détiens une référence à chaque chaîne dans le cache.

Rodney P. Barbati
la source
Non, toutes les chaînes égales internes qui sont en mémoire à un certain moment seront toujours le même objet. Il s'agira d'un objet différent de la chaîne égale qui était en mémoire avant d'être récupérée. Mais ce n'est pas un problème car l'ancienne chaîne n'est plus là.
bdruemen
0

Le coût de l'internement d'une chaîne est bien plus long que le temps gagné dans une seule comparaison stringA.equals (B). 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 économie.

Je suggérerais d'utiliser l'internement de chaînes pour modifier les performances lorsque vous optimisez des parties spécifiques de votre code.

Rappelez-vous également que les cordes sont immuables et ne font pas l'erreur idiote de

String a = SOME_RANDOM_VALUE
a.intern()

n'oubliez pas de faire

String a = SOME_RANDOM_VALUE.intern()
grumblebee
la source
0

Si vous recherchez un remplacement illimité de String.intern, également récupéré, les éléments suivants fonctionnent 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 approximativement estimer le nombre de chaînes différentes, utilisez simplement String.intern () avec -XX: StringTableSize = highEnoughValue .

bdruemen
la source
SoftRef ferait plus de sens.
Vach
@vach En utilisant WeakReference (au lieu de SoftReference), la mémoire est libérée plus tôt afin que d'autres allocations puissent aller plus vite. Cela dépend de ce que l'application fait d'autre, l'une ou l'autre pourrait avoir un sens.
bdruemen