По сути, выполнение String.intern() для ряда строк гарантирует, что все строки с одинаковым содержимым совместно используют одну и ту же память. Итак, если у вас есть список имен, в котором 'john' встречается 1000 раз, путем интернирования вы гарантируете, что только одному 'john' фактически выделена память.
Это может быть полезно для снижения требований к памяти вашей программы. Но имейте в виду, что кэш поддерживается JVM в постоянном пуле памяти, размер которого обычно ограничен по сравнению с кучей, поэтому вам не следует использовать intern, если у вас не слишком много повторяющихся значений.
Подробнее об ограничениях памяти при использовании intern()
С одной стороны, это правда, что вы можете удалить дубликаты строк, интернализировав их. Проблема в том, что интернализованные строки передаются в постоянную генерацию, которая является областью JVM, зарезервированной для объектов, не являющихся пользовательскими, таких как классы, методы и другие внутренние объекты JVM. Размер этой области ограничен и обычно намного меньше кучи. Вызов intern() для строки приводит к перемещению ее из кучи в постоянную генерацию, и вы рискуете исчерпать пространство PermGen.
В JDK 7 (я имею в виду в HotSpot) что-то изменилось.
В JDK 7 интернированные строки больше не выделяются в постоянной генерации кучи Java, а вместо этого выделяются в основной части кучи Java (известной как молодое и старое поколения) вместе с другими объектами, созданными приложением. Это изменение приведет к тому, что больше данных будет храниться в основной куче Java и меньше данных в постоянной генерации, и, таким образом, может потребоваться корректировка размеров кучи. Большинство приложений увидят лишь относительно небольшие различия в использовании кучи из-за этого изменения, но более крупные приложения, которые загружают много классов или интенсивно используют метод String.intern(), увидят более существенные различия.
Если вы хотите сравнить строки, вам следует использовать equals(). Выше будет выведено равно, потому что testString уже интернировано для вас компилятором. Вы можете интернировать строки самостоятельно, используя метод intern, как показано в предыдущих ответах....
Ответ 3
JLS
JLS 7 3.10.5 определяет это и приводит практический пример:
Более того, строковый литерал всегда ссылается на один и тот же экземпляр класса String . Это происходит потому, что строковые литералы - или, в более общем смысле, строки, являющиеся значениями постоянных выражений (§15.28) - "интернируются" таким образом, чтобы совместно использовать уникальные экземпляры, используя метод String.intern .
ВJVMS 7 5.1 говорится, что интернирование реализуется волшебным образом и эффективно с помощью выделенной CONSTANT_String_info структуры (в отличие от большинства других объектов, которые имеют более общие представления):
Строковый литерал представляет собой ссылку на экземпляр класса 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, чтобы увидеть интернирование в действии.
0 и 3: загружается одна и та же ldc #2 константа (литералы)
12: создается новый экземпляр string (с #2 в качестве аргумента)
35: a и c сравниваются как обычные объекты с if_acmpne
Представление постоянных строк в байт-коде - это настоящее волшебство:
он имеет специальную структуру CONSTANT_String_info, в отличие от обычных объектов (например, new String)
struct указывает на структуру CONSTANT_Utf8_info, которая содержит данные. Это единственные необходимые данные для представления строки.
и приведенная выше цитата из JVMS, кажется, говорит о том, что всякий раз, когда Utf8, на который указано, совпадает, идентичные экземпляры загружаются с помощью ldc.
Я выполнил аналогичные тесты для полей, и:
static final String s = "abc" указывает на таблицу констант через атрибут ConstantValue
поля, не являющиеся конечными, не имеют этого атрибута, но все равно могут быть инициализированы с помощью ldc
Вывод: существует прямая поддержка байт-кода для пула строк, и представление в памяти эффективно.
Бонус: сравните это с целочисленным пулом, который не имеет прямой поддержки байт-кода (т. Е. Нет CONSTANT_String_info аналога).
По сравнению с Java 7 размер пула строк в куче увеличен. Следовательно, у вас больше места для интернированных строк, но у вас меньше памяти для всего приложения.
Еще одна вещь, вы уже знаете, что при сравнении 2 объектов (ссылок) в Java '==' используется для сравнения ссылки на объект, 'equals' используется для сравнения содержимого объекта.