JLS
JLS 7 3.10.5 bunu tanımlar ve pratik bir örnek verir:
Ayrıca, bir dizgi değişmezi her zaman String sınıfının aynı örneğini belirtir. Bunun nedeni, dize değişmezlerinin - veya daha genel olarak, sabit ifadelerin (§15.28) değerleri olan dizelerin - String.intern yöntemini kullanarak benzersiz örnekleri paylaşmak için "interned" olmasıdır.
Örnek 3.10.5-1. Dize Değişmezleri
Derleme biriminden oluşan program (§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"; }
ve derleme birimi:
package other;
public class Other { public static String hello = "Hello"; }
çıktı üretir:
true true true true false true
JVM'lerle
JVMS 7 5.1, stajın özel bir CONSTANT_String_info
yapı ile sihirli ve verimli bir şekilde uygulandığını söylüyor (daha genel temsilleri olan diğer nesnelerin aksine):
Dize değişmezi, String sınıfının bir örneğine başvurudır ve bir sınıfın veya arabirimin ikili gösterimindeki CONSTANT_String_info yapısından (§4.4.3) türetilir. CONSTANT_String_info yapısı, dizgi değişmezini oluşturan Unicode kod noktalarının sırasını verir.
Java programlama dili, aynı dize değişmezlerinin (yani, aynı kod noktaları sırasını içeren değişmez değerlerin) aynı Dize sınıfı örneğine (JLS §3.10.5) başvurmasını gerektirir. Ayrıca, String.intern yöntemi herhangi bir dizede çağrılırsa, sonuç, bu dize değişmez olarak görünürse döndürülecek aynı sınıf örneğine başvurudur. Bu nedenle, aşağıdaki ifade true değerine sahip olmalıdır:
("a" + "b" + "c").intern() == "abc"
Bir dizgi değişmezi türetmek için Java Sanal Makinesi, CONSTANT_String_info yapısı tarafından verilen kod noktalarının sırasını inceler.
String.intern yöntemi daha önce CONSTANT_String_info yapısı tarafından verilenle aynı Unicode kod noktaları dizisini içeren bir sınıf String örneğinde çağrılmışsa, dizgi değişmezinin türetilmesinin sonucu, sınıf String'in aynı örneğine bir başvurudur.
Aksi takdirde, CONSTANT_String_info yapısı tarafından verilen Unicode kod noktalarının sırasını içeren yeni bir String sınıfı örneği oluşturulur; bu sınıf örneğine başvuru, dizgi değişmezinin türetilmesinin sonucudur. Son olarak, yeni String örneğinin stajyer yöntemi çağrılır.
Bytecode
Stajyerin nasıl çalıştığını görmek için bazı OpenJDK 7 bayt kodunu kodalım.
Biz kodalar:
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);
}
}
sürekli havuzumuz var:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
ve 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
Nasıl yapıldığına dikkat edin:
0
ve 3
: aynı ldc #2
sabit yüklenir (değişmez değerler)
12
: yeni bir dize örneği oluşturulur ( #2
argüman olarak)
35
: a
ve c
normal nesnelerle karşılaştırılır.if_acmpne
Sabit dizelerin temsili bayt kodunda oldukça büyülüdür:
ve yukarıdaki JVMS alıntısı, Utf8'in işaret ettiği her zaman aynı olduğunda, aynı örneklerin yüklendiğini söylüyor ldc
.
Alanlar için benzer testler yaptım ve:
static final String s = "abc"
ConstantValue Özelliği aracılığıyla sabit tabloyu gösterir
- nihai olmayan alanlar bu özelliğe sahip değildir, ancak yine de ile başlatılabilir
ldc
Sonuç : dize havuzu için doğrudan bayt kodu desteği vardır ve bellek gösterimi etkilidir.
Bonus: bunu doğrudan bayt kodu desteği olmayan (yani analog olmayan ) Tamsayı havuzuyla karşılaştırın CONSTANT_String_info
.