JLS
Le concept est appelé "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 String. En effet, les littéraux de chaîne - ou, plus généralement, les chaînes qui sont les valeurs d'expressions constantes (§15.28) - sont "internés" de manière à partager des instances uniques, en utilisant la méthode String.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(String[] args) {
String 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 String hello = "Hello"; }
et l'unité de compilation:
package other;
public class Other { public static String 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 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 de 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:
0
et 3
: la même ldc #2
constante est chargée (les littéraux)
12
: une nouvelle instance de chaîne est créée (avec #2
comme argument)
35
: a
et c
sont 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
Conclusion : le pool de chaînes prend directement en charge le bytecode et la représentation en mémoire est efficace.
Bonus: comparez cela au pool Integer , qui n'a pas de prise en charge directe du bytecode (c'est-à-dire pas d' CONSTANT_String_info
analogue).