Qu’est-ce que Java Ssortingng Interning?

Qu’est-ce que Ssortingng Interning en Java, quand dois-je l’utiliser, et pourquoi?

http://docs.oracle.com/javase/7/docs/api/java/lang/Ssortingng.html#intern ()

Faire de Ssortingng.intern () sur une série de chaînes fera en sorte que toutes les chaînes ayant le même contenu partagent la même mémoire. Donc, si vous avez une liste de noms où «John» apparaît 1000 fois, en vous internant, vous vous assurez qu’un seul «John» est réellement affecté à la mémoire.

Cela peut être utile pour réduire les besoins en mémoire de votre programme. Mais sachez que le cache est maintenu par JVM dans un pool de mémoire permanente, dont la taille est généralement limitée par rapport au tas, vous ne devez donc pas utiliser intern si vous n’avez pas trop de valeurs en double.


Plus d’informations sur les contraintes de mémoire liées à l’utilisation de stag ()

D’une part, il est vrai que vous pouvez supprimer les doublons de chaînes en les internalisant. Le problème est que les chaînes internalisées vont à la génération permanente, qui est une zone de la machine virtuelle Java réservée aux objects non-utilisateurs, tels que les classes, les méthodes et d’autres objects JVM internes. La taille de cette zone est limitée et est généralement beaucoup plus petite que le tas. L’appel de intern () sur une chaîne a pour effet de le déplacer du tas vers la génération permanente, et vous risquez de manquer d’espace PermGen.

– De: http://www.codeinstructions.com/2009/01/busting-javalangssortingngintern-myths.html


Depuis JDK 7 (je veux dire dans HotSpot), quelque chose a changé.

Dans JDK 7, les chaînes internes ne sont plus allouées dans la génération permanente du segment de mémoire Java, mais sont plutôt allouées dans la partie principale du segment de mémoire Java (appelées générations jeunes et anciennes), avec les autres objects créés par l’application. . Cette modification se traduira par plus de données résidant dans le segment de mémoire principal Java et moins de données dans la génération permanente, ce qui peut nécessiter un ajustement de la taille des segments. 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 volumineuses qui chargent de nombreuses classes ou qui utilisent beaucoup la méthode Ssortingng.intern () verront des différences plus significatives.

– À partir des fonctionnalités et améliorations de Java SE 7

Mise à jour: les chaînes internes sont stockées dans le tas principal à partir de Java 7 et suivants. http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

Il y a des questions “d’interview accrocheuse” pour lesquelles vous obtenez

 Ssortingng s1 = "testSsortingng"; Ssortingng s2 = "testSsortingng"; if(s1 == s2)System.out.println("equals!"); 

Si vous devez comparer les chaînes que vous devez utiliser, vous devez utiliser equals() . Ce qui précède imprimera égal, car le testSsortingng déjà la testSsortingng pour vous. Vous pouvez interner les chaînes vous-même en utilisant la méthode interne, comme indiqué dans les réponses précédentes ….

JLS

JLS 7 3.10.5 le définit et donne un exemple pratique:

De plus, un littéral de chaîne fait toujours référence à la même instance de la classe Ssortingng. C’est parce que les littéraux de chaîne – ou, plus généralement, les chaînes qui sont les valeurs des expressions constantes (§15.28) – sont “internés” de manière à partager des instances uniques, en utilisant la méthode Ssortingng.intern.

Exemple 3.10.5-1. Littéraux de chaîne

Le programme composé de l’unité de compilation (§7.3):

 package testPackage; class Test { public static void main(Ssortingng[] args) { Ssortingng hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static Ssortingng hello = "Hello"; } 

et l’unité de compilation:

 package other; public class Other { public static Ssortingng hello = "Hello"; } 

produit la sortie:

 true true true true false true 

JVMS

JVMS 7 5.1 dit que l’internement est implémenté comme par magie et avec une structure CONSTANT_Ssortingng_info dédiée (contrairement à la plupart des autres objects qui ont des représentations plus génériques):

Un littéral de chaîne est une référence à une instance de classe Ssortingng et est dérivé d’une structure CONSTANT_Ssortingng_info (§4.4.3) dans la représentation binary d’une classe ou d’une interface. La structure CONSTANT_Ssortingng_info donne la séquence de points de code Unicode constituant le littéral de chaîne.

Le langage de programmation Java requirejs que les littéraux de chaîne identiques (c’est-à-dire les littéraux contenant la même séquence de points de code) doivent faire référence à la même instance de classe Ssortingng (JLS §3.10.5). De plus, si la méthode Ssortingng.intern est appelée sur une chaîne quelconque, 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_Ssortingng_info.

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

  • Sinon, une nouvelle instance de classe Ssortingng est créée contenant la séquence de points de code Unicode donnée par la structure CONSTANT_Ssortingng_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 intern de la nouvelle instance Ssortingng est appelée.

Bytecode

Décompilons un bytecode OpenJDK 7 pour voir les internements en action.

Si on décomstack:

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

nous avons sur la réserve constante:

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

et main :

  0: ldc #2 // Ssortingng abc 2: astore_1 3: ldc #2 // Ssortingng abc 5: astore_2 6: new #3 // class java/lang/Ssortingng 9: dup 10: ldc #2 // Ssortingng abc 12: invokespecial #4 // Method java/lang/Ssortingng."":(Ljava/lang/Ssortingng;)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/Ssortingng;)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/Ssortingng;)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éée (avec l’argument #2 )
  • 35 : a et c sont comparés comme objects réguliers avec if_acmpne

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

  • il a une structure CONSTANT_Ssortingng_info dédiée, contrairement aux objects réguliers (par exemple new Ssortingng )
  • La structure pointe vers une structure CONSTANT_Utf8_info contenant les données. C’est la seule donnée nécessaire pour représenter la chaîne.

et la citation de JVMS ci-dessus semble dire que chaque fois que le Utf8 indiqué 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 Ssortingng s = "abc" pointe vers la table des constantes via l’ atsortingbut ConstantValue
  • les champs non finaux n’ont pas cet atsortingbut, mais peuvent toujours être initialisés avec ldc

Conclusion : le pool de chaînes prend en charge le bytecode direct et la représentation de la mémoire est efficace.

Bonus: comparez cela au pool Integer , qui ne prend pas directement en charge le bytecode (c.-à-d. Aucun analogue CONSTANT_Ssortingng_info ).

Mise à jour pour Java 8 ou plus . Dans Java 8, l’espace PermGen (Permanent Generation) est supprimé et remplacé par Meta Space. La mémoire du pool de chaînes est déplacée vers le tas de JVM.

Par rapport à Java 7, la taille du pool de chaînes est augmentée dans le tas. Par conséquent, vous avez plus d’espace pour les chaînes internalisées, mais vous avez moins de mémoire pour toute l’application.

Encore une chose, vous avez déjà su que lors de la comparaison de 2 (références de) objects en Java, « == » est utilisé pour comparer la référence de l’object, « equals » est utilisé pour comparer le contenu de l’object.

Vérifions ce code:

 Ssortingng value1 = "70"; Ssortingng value2 = "70"; Ssortingng value3 = new Integer(70).toSsortingng(); 

Résultat:

value1 == value2 —> vrai

value1 == value3 —> faux

value1.equals(value3) —> true

value1 == value3.intern() —> true

C’est pourquoi vous devez utiliser ‘ equals ‘ pour comparer 2 objects Ssortingng. Et c’est comme ça que le intern() est utile.

L’internalisation de chaînes est une technique d’optimisation par le compilateur. Si vous avez deux littéraux de chaîne identiques dans une unité de compilation, le code généré garantit qu’un seul object de chaîne est créé pour toute l’instance de ce littéral (caractères entre guillemets) dans l’assembly.

Je suis de C # background, donc je peux expliquer en donnant un exemple de cela:

 object obj = "Int32"; ssortingng str1 = "Int32"; ssortingng str2 = typeof(int).Name; 

sortie des comparaisons suivantes:

 Console.WriteLine(obj == str1); // true Console.WriteLine(str1 == str2); // true Console.WriteLine(obj == str2); // false !? 

Note 1 : Les objects sont comparés par référence.

Note 2 : typeof (int) .Name est évalué par la méthode de reflection pour ne pas être évalué au moment de la compilation. Ici, ces comparaisons sont faites au moment de la compilation.

Analyse des résultats: 1) true car ils contiennent tous deux le même littéral et donc le code généré ne contiendra qu’un seul object faisant référence à “Int32”. Voir la note 1 .

2) vrai car le contenu des deux est vérifié, ce qui est la même chose.

3) FALSE car str2 et obj n’ont pas le même littéral. Voir la note 2 .