Руководство по Java String Pool

Руководство по Java String Pool

1. обзор

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

В этой быстрой статье мы рассмотрим пул строк Java -the special memory region where Strings are stored by the JVM.

2. Стринг Интернат

Благодаря неизменностиStrings в Java, JVM может оптимизировать объем памяти, выделенной для них, наstoring only one copy of each literal String in the pool. Этот процесс называетсяinterning.

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

В случае обнаружения компилятор Java просто вернет ссылку на свой адрес памяти без выделения дополнительной памяти.

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

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

String constantString1 = "example";
String constantString2 = "example";

assertThat(constantString1)
  .isSameAs(constantString2);

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

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

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

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

String constantString = "example";
String newString = new String("example");

assertThat(constantString).isNotSameAs(newString);

4. String Литерал противString Object

When we create a String object using the new() operator, it always creates a new object in heap memory. On the other hand, if we create an object using String literal syntax e.g. “example”, it may return an existing object from the String pool, if it already exists. В противном случае он создаст новый объект String и поместит его в пул строк для повторного использования в будущем.

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

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

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

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

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

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

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

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

В общем,we should use the String literal notation when possible. Его легче читать, и это дает компилятору возможность оптимизировать наш код.

5. Ручное интернирование

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

ИнтернированиеString вручную сохранит его ссылку в пуле, и JVM вернет эту ссылку, когда это необходимо.

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

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

assertThat(constantString).isNotSameAs(newString);

String internedString = newString.intern();

assertThat(constantString)
  .isSameAs(internedString);

6. Вывоз мусора

До Java 7 JVMplaced the Java String Pool in the PermGen space, which has a fixed size — it can’t be expanded at runtime and is not eligible for garbage collection.

Риск интернированияStrings вPermGen (вместоHeap) заключается в том, чтоwe can get an OutOfMemory error из JVM, если мы интернируем слишком многоStrings.

Начиная с Java 7, пул строк Java составляетstored in the Heap space, which is garbage collected от JVM.. Преимущество этого подхода -reduced risk of OutOfMemory error, потому чтоStrings, на которые нет ссылки, будет удален из пула, тем самым освобождение памяти.

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

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

-XX:MaxPermSize=1G

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

-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics

Если мы хотим увеличить размер пула с точки зрения сегментов, мы можем использовать опцию JVMStringTableSize:

-XX:StringTableSize=4901

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

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

8. Замечание о Java 9

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

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

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

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

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

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