Рубрики
Без рубрики

Руководство по пулу строк Java

Узнайте, как JVM оптимизирует объем памяти, выделенный для хранения строк в пуле строк Java.

Автор оригинала: baeldung.

1. Обзор

Объект String является наиболее часто используемым классом в языке Java.

В этой краткой статье мы рассмотрим пул строк Java — специальную область памяти, где Строки хранятся в JVM .

2. Интернирование строк

Благодаря неизменяемости Строк в Java JVM может оптимизировать объем выделенной для них памяти, храня только одну копию каждого литерала Строки в пуле . Этот процесс называется интернированием .

Когда мы создаем переменную String и присваиваем ей значение, JVM ищет в пуле String равного значения.

Если он будет найден, компилятор Java просто вернет ссылку на свой адрес памяти, не выделяя дополнительной памяти.

Если он не найден, он будет добавлен в пул (интернет), и его ссылка будет возвращена.

Давайте напишем небольшой тест, чтобы проверить это:

String constantString1 = "Baeldung";
String constantString2 = "Baeldung";
        
assertThat(constantString1)
  .isSameAs(constantString2);

3. Строки, выделенные с помощью конструктора

Когда мы создаем Строку с помощью оператора new , компилятор Java создаст новый объект и сохранит его в пространстве кучи, зарезервированном для JVM.

Каждая строка |, созданная подобным образом, будет указывать на другую область памяти со своим собственным адресом.

Давайте посмотрим, чем это отличается от предыдущего случая:

String constantString = "Baeldung";
String newString = new String("Baeldung");
 
assertThat(constantString).isNotSameAs(newString);

4. Строковый литерал против строкового объекта

Когда мы создаем объект String с помощью оператора new () , он всегда создает новый объект в памяти кучи. С другой стороны, если мы создадим объект с использованием синтаксиса String literal, например “Baeldung”, он может вернуть существующий объект из пула строк, если он уже существует. В противном случае он создаст новый строковый объект и поместит его в пул строк для последующего повторного использования.

На высоком уровне оба являются объектами String , но основное различие заключается в том, что оператор new() всегда создает новый объект String . Кроме того, когда мы создаем Строку с помощью литерала – он интернируется.

Это будет гораздо более ясно, когда мы сравним два объекта String , созданных с помощью String литерала и нового оператора:

String first = "Baeldung"; 
String second = "Baeldung"; 
System.out.println(first == second); // True

В этом примере объекты String будут иметь одну и ту же ссылку.

Затем давайте создадим два разных объекта с помощью new и проверим, что у них разные ссылки:

String third = new String("Baeldung");
String fourth = new String("Baeldung"); 
System.out.println(third == fourth); // False

Аналогично, когда мы сравниваем литерал String с объектом String , созданным с помощью оператора new () , он возвращает false:

String fifth = "Baeldung";
String sixth = new String("Baeldung");
System.out.println(fifth == sixth); // False

В общем, мы должны использовать строковое литеральное обозначение, когда это возможно . Это легче читать, и это дает компилятору возможность оптимизировать наш код.

5. Ручная Стажировка

Мы можем вручную ввести String в пул строк Java, вызвав метод intern() для объекта, который мы хотим интернировать.

Ручное интернирование строки | сохранит ее ссылку в пуле, и JVM вернет эту ссылку при необходимости.

Давайте создадим тестовый случай для этого:

String constantString = "interned Baeldung";
String newString = new String("interned Baeldung");

assertThat(constantString).isNotSameAs(newString);

String internedString = newString.intern();

assertThat(constantString)
  .isSameAs(internedString);

6. Сбор Мусора

До Java 7 JVM помещала пул строк Java в пространство PermGen , которое имеет фиксированный размер — оно не может быть расширено во время выполнения и не подходит для сборки мусора .

Риск интернирования Строк в PermGen (вместо кучи ) заключается в том, что мы можем получить OutOfMemory ошибку из JVM, если мы интернируем слишком много строк .

Начиная с Java 7, пул строк Java хранится в пространстве Heap , которое является мусором, собранным JVM . Преимуществом этого подхода является снижение риска OutOfMemory ошибки , поскольку не связанные Строки будут удалены из пула, тем самым освободив память.

7. Производительность и оптимизация

В Java 6 единственная оптимизация, которую мы можем выполнить, – это увеличение пространства PermGen во время вызова программы с помощью параметра MaxPermSize JVM:

-XX:MaxPermSize=1G

В Java 7 у нас есть более подробные параметры для изучения и расширения/уменьшения размера пула. Давайте рассмотрим два варианта просмотра размера пула:

-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics

Если мы хотим увеличить размер пула с точки зрения ведер, мы можем использовать параметр StringTableSize JVM:

-XX:StringTableSize=4901

До Java 7u40 размер пула по умолчанию составлял 1009 ведер, но это значение было подвержено нескольким изменениям в более поздних версиях Java. Если быть точным, размер пула по умолчанию с Java 7u40 до Java 11 составлял 60013, а теперь он увеличился до 65536.

Обратите внимание, что увеличение размера пула будет потреблять больше памяти, но имеет преимущество в сокращении времени, необходимого для вставки Строки в стол.

8. Примечание О Java 9

До Java 8 Строки были внутренне представлены в виде массива символов – char [] , закодированных в UTF-16 , так что каждый символ использует два байта памяти.

В Java 9 предоставляется новое представление, называемое Compact Strings. Этот новый формат будет выбирать соответствующую кодировку между char[] и byte[] в зависимости от сохраненного содержимого.

Поскольку новое представление String будет использовать кодировку UTF-16 только в случае необходимости, объем кучи памяти будет значительно ниже, что, в свою очередь, приведет к меньшим затратам Сборщика мусора на JVM.

9. Заключение

В этом руководстве мы показали, как JVM и компилятор Java оптимизируют выделение памяти для объектов String через пул строк Java.

Все примеры кода, используемые в статье, доступны на GitHub .