JLS
Концепция называется "интернирование" в JLS.
Соответствующий отрывок из JLS 7 3.10.5 :
Кроме того, строковый литерал всегда ссылается на один и тот же экземпляр класса String.Это связано с тем, что строковые литералы - или, в более общем смысле, строки, являющиеся значениями константных выражений (§15.28) - «интернированы», чтобы обмениваться уникальными экземплярами, используя метод String.intern.
Пример 3.10.5-1.Строковые литералы
Программа, состоящая из модуля компиляции (§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"; }
и модуля компиляции:
package other;
public class Other { public static String hello = "Hello"; }
производит вывод:
true true true true false true
JVMS
JVMS 7 5.1 говорит :
Строковый литерал является ссылкой на экземпляр классаString, и является производным от структуры CONSTANT_String_info (§4.4.3) в двоичном представлении класса или интерфейса.Структура CONSTANT_String_info дает последовательность кодовых точек Unicode, составляющих строковый литерал.
Язык программирования Java требует, чтобы идентичные строковые литералы (то есть литералы, которые содержат одинаковую последовательность кодовых точек) должны ссылаться на один и тот же экземпляркласса String (JLS §3.10.5).Кроме того, если метод String.intern вызывается для какой-либо строки, результатом является ссылка на тот же экземпляр класса, который будет возвращен, если эта строка появится в виде литерала.Таким образом, следующее выражение должно иметь значение true:
("a" + "b" + "c").intern() == "abc"
Для получения строкового литерала виртуальная машина Java проверяет последовательность кодовых точек, заданных структурой CONSTANT_String_info.
Если метод String.intern ранее вызывался для экземпляра класса String, содержащего последовательность кодовых точек Unicode, идентичную той, которая задана структурой CONSTANT_String_info, то результатом строкового литерала является ссылка на этот же экземпляркласса String.
В противном случае создается новый экземпляр класса String, содержащий последовательность кодовых точек Unicode, заданную структурой CONSTANT_String_info;ссылка на этот экземпляр класса является результатом строкового литерала.Наконец, вызывается метод intern нового экземпляра String.
Байт-код
Также полезно взглянуть на реализацию байт-кода в OpenJDK 7.
Если мы декомпилируем:
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);
}
}
у нас в постоянном пуле:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
и 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
Обратите внимание, как:
0
и 3
: загружена одинаковая константа ldc #2
(литералы) 12
: создан новый экземпляр строки (с * 1074)* в качестве аргумента) 35
: a
и c
сравниваются как обычные объекты с if_acmpne
Представление константных строк довольно волшебно в байт-коде:
- имеет выделенную структуру CONSTANT_String_info , в отличие от обычных объектов (например,
new String
) - структура указывает на структуру CONSTANT_Utf8_info который содержит данные.Это единственные необходимые данные для представления строки.
, и приведенная выше цитата из JVMS, похоже, говорит, что всякий раз, когда Utf8, на который указывает указатель, совпадает, идентичные экземпляры загружаются ldc
. * 1098.*
Я провел аналогичные тесты для полей и:
static final String s = "abc"
указывает на таблицу констант через атрибут ConstantValue - не финальнополя не имеют этого атрибута, но все еще могут быть инициализированы с помощью
ldc
Заключение : есть прямая поддержка байт-кода для пула строк, и представление памяти эффективно.
Бонус: сравните это с Целочисленным пулом , который не имеет прямой поддержки байт-кода (т. Е. Нет CONSTANT_String_info
аналога).