Java 9: Сompact Strings.



По данным OpenJDK не менее 25% объектов в памяти занимают строки. По той же статистике 95% строк содержат только латинские символы и цифры, числовое значение которых умещается в 1 байт, а 5-15% памяти заняты бесполезными нулями.



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



В Java 9 вышло обновление Compact Strings, которое меняет структуру хранения строки.

Символы теперь лежат в byte[] и хранятся в одной из двух кодировок. Сама кодировка записана в новом параметре coder. Возможны 2 варианта:

1️⃣ Если все символы строки умещаются в 1 байт(латиница и цифры), то записываются в одну ячейку массива. Выставляется кодировка Latin-1.

2️⃣ Если хотя бы один символ требует 16 байт, все элементы занимают 2 ячейки массива. Выставляется кодировка UTF-16.



Можно ли было перевести строки на UTF-8, символы с переменной длиной?

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



Строки в разных кодировках по-разному лежат в памяти, и работать с ними нужно тоже по-разному. Каждый метод в классе String начинается с проверки кодировки и разделяется на две ветки — для Latin-1 и UTF-16. Их код вынесен в отдельные классы StringLatin1 и StringUTF16.



Память сэкономили, кода стало в 3 раза больше, не упала ли производительность?

Любая дополнительная проверка снижает скорость обработки, особенно при работе с маленькими строками. Поэтому на уровне JVM и JIT добавлены оптимизации проверки кодировки и сравнения строк, изменён механизм конкатенации и других операций. Именно за счёт внутренних оптимизаций компактные строки работают в среднем на 20% быстрее и создают на 30% меньше промежуточных объектов.



Самое главное - эти изменения никак не отразились на интерфейсе String. I/O классы, StringBuilder, StringBuffer тоже адаптированы без внешних изменений. Нужно просто перейти на java 9 и приложение будет занимать на 5-15% меньше памяти.