Когда Java-строки интернированы?

Вдохновленный комментариями к этому вопросу, я уверен, что Java String интернирован во время выполнения, а не компилирует время - несомненно, только тот факт, что классы могут быть скомпилированы в разное время, но все равно укажут на ту же ссылку во время выполнения.

Я не могу найти никаких доказательств, подтверждающих это. Кто-нибудь может это оправдать?

Ответ 1

Оптимизация происходит (или, по крайней мере, может случиться) в обоих местах:

  • Если в одном классе появляются две ссылки на одну и ту же строчную константу, я ожидаю, что файл класса будет содержать только одну запись пула констант. Это не требуется строго для того, чтобы создать только один объект String, созданный в JVM, но это очевидная оптимизация. Это фактически не интернирование, а просто постоянная оптимизация.
  • Когда классы загружаются, пул строк для класса добавляется в общий пул. Это "реальное" интернирование.

(У меня есть неопределенное воспоминание о том, что один из битов работы для Java 7 вокруг "маленьких файлов jar" включал в себя единственный пул строк для всего файла jar... но я мог бы ошибаться.)

EDIT: раздел 5.1 спецификации JVM, "Пул постоянной продолжительности выполнения" подробно описывает это:

Чтобы получить строковый литерал, Java виртуальная машина проверяет последовательность символов, заданных Структура CONSTANT_String_info.

  • Если метод String.intern имеет ранее вызывался на экземпляр класса String, содержащего последовательность символов Unicode, идентичных что задано CONSTANT_String_info структуры, то результат строки литеральный вывод является ссылкой на тот же экземпляр класса String.

  • В противном случае новый экземпляр класса Строка создается, содержащая последовательность символов Unicode структурой CONSTANT_String_info; этот экземпляр класса является результатом строковый литерал. В заключение, метод intern новой строки экземпляр вызывается.

Ответ 2

Runtime.

Спецификации JLS и JVM определяют javac-компиляцию для файлов классов, которые содержат константные объявления (в Constant Pool) и постоянное использование в коде (который javac может встраивать как ссылочные значения примитива/объекта). Для констант String для компиляции компилятор генерирует код для создания экземпляров String и для вызова String.intern() для них, так что константы JVM String автоматически устанавливаются. Это поведенческое требование от JLS:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28

Выражения константы времени компиляции типа String всегда "интернированы", чтобы обмениваться уникальными экземплярами, используя метод String.intern.

Но эти спецификации не имеют ни понятия, ни определения каких-либо конкретных структур пула/ссылок/дескрипторов String, будь то время компиляции или время выполнения. (Конечно, в общем случае спецификация JVM не предусматривает какой-либо конкретной внутренней структуры для объектов: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.7)

Причина, по которой не упоминаются структуры пула пула, состоит в том, что они полностью обрабатываются классом String. Внутренний пул представляет собой частную статическую/классную структуру класса String (неопределенный спецификациями JLS и JVM и javadoc).

Объекты добавляются в внутренний пул при вызове String.intern() во время выполнения. Внутренний пул используется в частном порядке классом String - когда код создает новые экземпляры String и вызывает String.intern(), класс String определяет, следует ли повторно использовать существующие внутренние данные. Оптимизация может выполняться компилятором JIT во время выполнения.

Здесь нет вклада времени компиляции, запретите ванильную вставку постоянных значений.