JLS
Il concetto è chiamato "interning" da JLS.
Passaggio pertinente di JLS 7 3.10.5 :
Inoltre, un letterale stringa si riferisce sempre alla stessa istanza della classe String. Questo perché i letterali di stringa - o, più in generale, stringhe che sono i valori di espressioni costanti (§15.28) - vengono "internati" in modo da condividere istanze univoche, usando il metodo String.intern.
Esempio 3.10.5-1. Letterali a corda
Il programma costituito dall'unità di compilazione (§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"; }
e l'unità di compilazione:
package other;
public class Other { public static String hello = "Hello"; }
produce l'output:
true true true true false true
JVM
JVMS 7 5.1 dice :
Un letterale stringa è un riferimento a un'istanza della classe String ed è derivato da una struttura CONSTANT_String_info (§4.4.3) nella rappresentazione binaria di una classe o interfaccia. La struttura CONSTANT_String_info fornisce la sequenza di punti di codice Unicode che costituisce la stringa letterale.
Il linguaggio di programmazione Java richiede che i letterali stringa identici (ovvero i letterali che contengono la stessa sequenza di punti di codice) debbano fare riferimento alla stessa istanza della classe String (JLS §3.10.5). Inoltre, se il metodo String.intern viene chiamato su qualsiasi stringa, il risultato è un riferimento alla stessa istanza della classe che verrebbe restituita se quella stringa apparisse come un valore letterale. Pertanto, la seguente espressione deve avere il valore vero:
("a" + "b" + "c").intern() == "abc"
Per derivare letteralmente una stringa, la Java Virtual Machine esamina la sequenza di punti di codice forniti dalla struttura CONSTANT_String_info.
Se il metodo String.intern è stato precedentemente chiamato su un'istanza della classe String contenente una sequenza di punti di codice Unicode identici a quelli forniti dalla struttura CONSTANT_String_info, il risultato della derivazione letterale di stringa è un riferimento a quella stessa istanza della classe String.
Altrimenti, viene creata una nuova istanza della classe String contenente la sequenza di punti di codice Unicode forniti dalla struttura CONSTANT_String_info; un riferimento a quell'istanza di classe è il risultato della derivazione letterale di stringa. Infine, viene invocato il metodo intern della nuova istanza String.
bytecode
È anche istruttivo esaminare l'implementazione del bytecode su OpenJDK 7.
Se decompiliamo:
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);
}
}
abbiamo nel pool costante:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
e 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
Nota come:
0
e 3
: ldc #2
viene caricata la stessa costante (i letterali)
12
: viene creata una nuova istanza di stringa (con #2
come argomento)
35
: a
e c
vengono confrontati come oggetti regolari conif_acmpne
La rappresentazione di stringhe costanti è piuttosto magica sul bytecode:
- ha una struttura CONSTANT_String_info dedicata , diversamente dagli oggetti normali (es
new String
)
- la struttura punta a una CONSTANT_Utf8_info struttura che contiene i dati. Questi sono gli unici dati necessari per rappresentare la stringa.
e la citazione JVMS sopra sembra dire che ogni volta che Utf8 indicato è lo stesso, vengono caricate istanze identiche ldc
.
Ho fatto test simili per i campi e:
static final String s = "abc"
punta alla tabella costante tramite l' attributo ConstantValue
- i campi non finali non hanno quell'attributo, ma possono comunque essere inizializzati con
ldc
Conclusione : esiste un supporto bytecode diretto per il pool di stringhe e la rappresentazione della memoria è efficiente.
Bonus: confrontalo con il pool Integer , che non ha il supporto del bytecode diretto (cioè nessun CONSTANT_String_info
analogo).