Как JVM работает со строками, часть 1



По статистике OpenJDK в приложениях 15-50% памяти занимают экземпляры String. Это много, поэтому разработчики JVM прикладывают много усилий, чтобы оптимизировать работу со строками.



Как сэкономить память, если в системе миллионы экземпляров?



1️⃣ Уменьшить количество памяти под одну строку.



Мы уже разбирали этот случай: в java 9 вышло обновление Compact String и размер памяти под строки сократился до 2 раз.



2️⃣ Добавить кэширование.



Есть два способа создать экземпляр String:

🔸Через оператор new:

String s = new String ("one");

В памяти создаётся новый объект.



🔸Без оператора new:

String s = "one";



В этом случае идёт работа с кэшем строк под названием String pool. Схема работы такая:

▫️Проверить в String Pool, есть ли там такая строка.

▫️Если есть — вернуть её.

▫️Если нет — создать объект, поместить в кэш, вернуть.



Как реализован String pool?

Структура похожа на HashMap: хэш-таблица фиксированного размера с парами хэш — строка. В последних версиях java она занимает 64 МБ, при желании размер меняется с помощью флажка:

-XX:StringTableSize=65536



В следующем посте рассмотрим ещё два способа снижения издержек на строки.



Ответ на 1 вопрос перед постом:

new String("Java") создаёт новый объект в хипе,

s2 = "Java" создаёт объект в String pool. Это два разных объекта, поэтому при сравнении через == результат будет false.



Можно исправить ситуацию и добавить строчку в кэш методом intern():

String s1="Java";

String s2=new String("Java");

s2=s2.intern();

println(s1==s2); //true



Рассмотрим второй вопрос. О том, сколько строк создаётся в конструкции

String str=new String("Java");



Подвох в том, что для конструктора String нужен экземпляр String, поэтому создаётся 2 строки — "Java" и new String;



Но если строка "Java" использовалась раньше и попала в кэш, то будет создана только одна строка — new String.