Qu’est-ce que le pool de chaînes Java et en quoi «s» est-il différent de la nouvelle chaîne («s»)?

Qu’entend-on par Ssortingng Pool ? Et quelle est la différence entre les déclarations suivantes:

Ssortingng s = "hello"; Ssortingng s = new Ssortingng("hello"); 

Y a-t-il une différence entre le stockage de ces deux chaînes par la JVM?

Le pool de chaînes est l’implémentation particulière du concept d’ internement de chaîne par la JVM:

En informatique, l’internalisation de chaînes est une méthode de stockage d’une seule copie de chaque valeur de chaîne distincte, qui doit être immuable. Interner des chaînes rend certaines tâches de traitement de chaînes plus efficaces en termes de temps ou d’espace, au désortingment du temps nécessaire à la création ou à l’installation de la chaîne. Les valeurs distinctes sont stockées dans un pool de chaînes.

Fondamentalement, un pool de chaînes interne permet à un environnement d’exécution d’économiser de la mémoire en préservant des chaînes immuables dans un pool afin que les zones de l’application puissent réutiliser des instances de chaînes communes au lieu d’en créer plusieurs.

En guise de remarque intéressante, l’internement de chaîne est un exemple du modèle de conception de la masselotte :

Flyweight est un modèle de conception de logiciel. Un poids mouche est un object qui minimise l’utilisation de la mémoire en partageant autant de données que possible avec d’autres objects similaires; c’est un moyen d’utiliser des objects en grand nombre lorsqu’une simple représentation répétée utiliserait une quantité de mémoire inacceptable.

Le pool de chaînes permet de réutiliser les constantes de chaîne, ce qui est possible car les chaînes en Java sont immuables. Si vous répétez la même chaîne de manière constante dans votre code Java, vous ne pouvez avoir qu’une seule copie de cette chaîne dans votre système, ce qui constitue l’un des avantages de ce mécanisme.

Lorsque vous utilisez Ssortingng s = "ssortingng constant"; vous obtenez la copie qui se trouve dans le pool de chaînes. Toutefois, lorsque vous faites Ssortingng s = new Ssortingng("ssortingng constant"); vous forcez une copie à être allouée.

JLS

Comme mentionné par Andrew , le concept s’appelle “interning” par le JLS.

Passage pertinent de JLS 7 3.10.5 :

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 :

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

Il est également instructif d’examiner l’implémentation du bytecode sur OpenJDK 7.

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 ).

Les objects Ssortingng sont essentiellement des wrappers autour des littéraux de chaîne. Les objects de chaîne uniques sont regroupés pour empêcher la création d’object inutile et la machine virtuelle Java peut décider de regrouper des littéraux de chaîne en interne. Il existe également un support direct du bytecode pour les constantes Ssortingng qui sont référencées plusieurs fois, à condition que le compilateur le supporte.

Lorsque vous utilisez un littéral, dites Ssortingng str = "abc"; , l’object dans le pool est utilisé. Si vous utilisez Ssortingng str = new Ssortingng("abc"); , un nouvel object est créé, mais le littéral de chaîne existant peut être réutilisé au niveau JVM ou au niveau du bytecode (au moment de la compilation).

Vous pouvez vérifier cela vous-même en créant beaucoup de chaînes dans une boucle for et en utilisant l’opérateur == pour vérifier l’égalité des objects. Dans l’exemple suivant, ssortingng.value est privé à Ssortingng et contient le littéral de chaîne utilisé. Parce qu’il est privé, il faut y accéder par reflection.

 public class InternTest { public static void main(Ssortingng[] args) { Ssortingng rehi = "rehi"; Ssortingng rehi2 = "rehi"; Ssortingng rehi2a = "not rehi"; Ssortingng rehi3 = new Ssortingng("rehi"); Ssortingng rehi3a = new Ssortingng("not rehi"); Ssortingng rehi4 = new Ssortingng(rehi); Ssortingng rehi5 = new Ssortingng(rehi2); Ssortingng rehi6 = new Ssortingng(rehi2a); Ssortingng[] arr = new Ssortingng[] { rehi, rehi2, rehi2a, rehi3, rehi3a, rehi4, rehi5, rehi6 }; Ssortingng[] arr2 = new Ssortingng[] { "rehi", "rehi (2)", "not rehi", "new Ssortingng(\"rehi\")", "new Ssortingng(\"not rehi\")", "new Ssortingng(rehi)", "new Ssortingng(rehi (2))", "new Ssortingng(not rehi)" }; Field f; try { f = Ssortingng.class.getDeclaredField("value"); f.setAccessible(true); } catch (NoSuchFieldException | SecurityException e) { throw new IllegalStateException(e); } for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { System.out.println("i: " +arr2[i]+", j: " +arr2[j]); System.out.println("i==j: " + (arr[i] == arr[j])); System.out.println("i equals j: " + (arr[i].equals(arr[j]))); try { System.out.println("i.value==j.value: " + (f.get(arr[i]) == f.get(arr[j]))); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } System.out.println("========"); } } } } 

Sortie:

 i: rehi, j: rehi i==j: true i equals j: true i.value==j.value: true ======== i: rehi, j: rehi (2) i==j: true i equals j: true i.value==j.value: true ======== i: rehi, j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: rehi, j: new Ssortingng("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new Ssortingng("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: rehi, j: new Ssortingng(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new Ssortingng(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new Ssortingng(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: rehi i==j: true i equals j: true i.value==j.value: true ======== i: rehi (2), j: rehi (2) i==j: true i equals j: true i.value==j.value: true ======== i: rehi (2), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: new Ssortingng("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new Ssortingng("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: new Ssortingng(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new Ssortingng(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new Ssortingng(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: not rehi i==j: true i equals j: true i.value==j.value: true ======== i: not rehi, j: new Ssortingng("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new Ssortingng("not rehi") i==j: false i equals j: true i.value==j.value: true ======== i: not rehi, j: new Ssortingng(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new Ssortingng(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new Ssortingng(not rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("rehi"), j: new Ssortingng("rehi") i==j: true i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: new Ssortingng("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("rehi"), j: new Ssortingng(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: new Ssortingng(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("rehi"), j: new Ssortingng(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: not rehi i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng("not rehi"), j: new Ssortingng("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: new Ssortingng("not rehi") i==j: true i equals j: true i.value==j.value: true ======== i: new Ssortingng("not rehi"), j: new Ssortingng(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: new Ssortingng(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng("not rehi"), j: new Ssortingng(not rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(rehi), j: new Ssortingng("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: new Ssortingng("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(rehi), j: new Ssortingng(rehi) i==j: true i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: new Ssortingng(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi), j: new Ssortingng(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(rehi (2)), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi (2)), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi (2)), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(rehi (2)), j: new Ssortingng("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi (2)), j: new Ssortingng("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(rehi (2)), j: new Ssortingng(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi (2)), j: new Ssortingng(rehi (2)) i==j: true i equals j: true i.value==j.value: true ======== i: new Ssortingng(rehi (2)), j: new Ssortingng(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: not rehi i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(not rehi), j: new Ssortingng("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: new Ssortingng("not rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new Ssortingng(not rehi), j: new Ssortingng(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: new Ssortingng(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: new Ssortingng(not rehi), j: new Ssortingng(not rehi) i==j: true i equals j: true i.value==j.value: true ======== 

Il est curieux que personne n’ait répondu directement à la question, mais la plupart des réponses ont beaucoup de commentaires positifs.

En résumé, le premier crée une entrée dans le pool de chaînes, qui peut être réutilisée (plus efficace en raison des liens ci-dessus sur l’immuabilité), et la seconde crée un nouvel object Ssortingng (plus coûteux).

Les deux objects vivent dans le tas. Les références aux deux seront dans la stack du thread.

http://www.journaldev.com/797/what-is-java-ssortingng-pool donne un aperçu clair de la façon dont cela est réalisé

La JVM effectue certaines manipulations lors de l’instanciation des littéraux de chaîne pour augmenter les performances et réduire les frais de mémoire. Pour réduire le nombre d’objects Ssortingng créés dans la JVM, la classe Ssortingng conserve un pool de chaînes. Chaque fois que votre code crée un littéral de chaîne, la machine virtuelle Java vérifie d’abord le pool de littéral de chaîne. Si la chaîne existe déjà dans le pool, une référence à l’instance regroupée est renvoyée. Si la chaîne n’existe pas dans le pool, un nouvel object Ssortingng est instancié, puis placé dans le pool.

  public class Program { public static void main(Ssortingng[] args) { Ssortingng str1 = "Hello"; Ssortingng str2 = "Hello"; System.out.print(str1 == str2); } } 

Sortie: true

Malheureusement, lorsque vous utilisez

 Ssortingng a=new Ssortingng("Hello"); 

L’object Ssortingng est créé à partir du pool de littéraux Ssortingng, même si une chaîne égale existe déjà dans le pool.

 public class Program { public static void main(Ssortingng[] args) { Ssortingng str1 = "Hello"; Ssortingng str2 = new Ssortingng("Hello"); System.out.print(str1 == str2 ); } } 

Sortie: false