JLS
JLS 7 3.10.5 definiert es und gibt ein praktisches Beispiel:
Darüber hinaus bezieht sich ein String-Literal immer auf dieselbe Instanz der Klasse String. Dies liegt daran, dass Zeichenfolgenliterale - oder allgemeiner Zeichenfolgen, die die Werte konstanter Ausdrücke sind (§15.28) - mit der Methode String.intern "interniert" werden, um eindeutige Instanzen gemeinsam zu nutzen.
Beispiel 3.10.5-1. String-Literale
Das Programm bestehend aus der Zusammenstellungseinheit (§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"; }
und die Zusammenstellungseinheit:
package other;
public class Other { public static String hello = "Hello"; }
erzeugt die Ausgabe:
true true true true false true
JVMS
Laut JVMS 7 5.1 wird das Internieren mit einer dedizierten CONSTANT_String_info
Struktur magisch und effizient implementiert (im Gegensatz zu den meisten anderen Objekten mit allgemeineren Darstellungen):
Ein String-Literal ist eine Referenz auf eine Instanz der Klasse String und wird aus einer CONSTANT_String_info-Struktur (§4.4.3) in der binären Darstellung einer Klasse oder Schnittstelle abgeleitet. Die Struktur CONSTANT_String_info gibt die Folge von Unicode-Codepunkten an, die das Zeichenfolgenliteral bilden.
Die Java-Programmiersprache erfordert, dass identische Zeichenfolgenliterale (dh Literale, die dieselbe Folge von Codepunkten enthalten) auf dieselbe Instanz der Klasse String verweisen müssen (JLS §3.10.5). Wenn die Methode String.intern für eine beliebige Zeichenfolge aufgerufen wird, ist das Ergebnis außerdem ein Verweis auf dieselbe Klasseninstanz, die zurückgegeben würde, wenn diese Zeichenfolge als Literal angezeigt würde. Daher muss der folgende Ausdruck den Wert true haben:
("a" + "b" + "c").intern() == "abc"
Um ein Zeichenfolgenliteral abzuleiten, untersucht die Java Virtual Machine die Reihenfolge der Codepunkte, die in der Struktur CONSTANT_String_info angegeben sind.
Wenn die Methode String.intern zuvor für eine Instanz der Klasse String aufgerufen wurde, die eine Folge von Unicode-Codepunkten enthält, die mit der in der Struktur CONSTANT_String_info angegebenen identisch sind, ist das Ergebnis der Ableitung des String-Literal ein Verweis auf dieselbe Instanz der Klasse String.
Andernfalls wird eine neue Instanz der Klasse String erstellt, die die von der Struktur CONSTANT_String_info angegebene Folge von Unicode-Codepunkten enthält. Ein Verweis auf diese Klasseninstanz ist das Ergebnis der Ableitung von Zeichenfolgenliteralen. Schließlich wird die interne Methode der neuen String-Instanz aufgerufen.
Bytecode
Lassen Sie uns einen OpenJDK 7-Bytecode dekompilieren, um die Internierung in Aktion zu sehen.
Wenn wir dekompilieren:
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);
}
}
Wir haben auf dem ständigen Pool:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
und 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
Beachten Sie, wie:
0
und 3
: die gleiche ldc #2
Konstante wird geladen (die Literale)
12
: Eine neue String-Instanz wird erstellt (mit #2
als Argument)
35
: a
und c
werden als reguläre Objekte mit verglichenif_acmpne
Die Darstellung konstanter Zeichenfolgen ist auf dem Bytecode ziemlich magisch:
- es verfügt über eine eigene CONSTANT_String_info Struktur, anders als bei normalen Objekten (zB
new String
)
- Die Struktur zeigt auf eine CONSTANT_Utf8_info-Struktur , die die Daten enthält. Dies sind die einzigen erforderlichen Daten, um die Zeichenfolge darzustellen.
und das obige JVMS-Zitat scheint zu sagen, dass immer dann, wenn der Utf8, auf den gezeigt wird, derselbe ist, identische Instanzen von geladen werden ldc
.
Ich habe ähnliche Tests für Felder durchgeführt und:
Schlussfolgerung : Es gibt eine direkte Bytecode-Unterstützung für den Zeichenfolgenpool und die Speicherdarstellung ist effizient.
Bonus: Vergleichen Sie das mit dem Integer-Pool , der keine direkte Bytecode-Unterstützung bietet (dh kein CONSTANT_String_info
Analogon).