Каковы преимущества компактных строк по сжатым строкам в JDK9?
Разница между компактными строками и сжатыми строками в Java 9
Ответ 1
Сжатые строки (Java 6) и компактные строки (Java 9) имеют одну и ту же мотивацию (строки часто эффективны Latin-1, поэтому половина пространства теряется) и цель (сделать эти строки небольшими), но реализации отличаются много.
Сжатые строки
В интервью Алексей Шипилев (который отвечал за реализацию функции Java 9) сказал об сжатых строках:
Функция UseCompressedStrings была довольно консервативной: при различении событий
char[]
иbyte[]
и попытке сжимать конструкциюchar[]
вbyte[]
onString
она выполняла большинство операцийString
наchar[]
, который требовал распаковатьString.
. Поэтому он использовал только специальный тип рабочих нагрузок, где большинство строк сжимаемо (так что сжатие не пропадает даром), и на них выполняется только ограниченное количество известных операцийString
(поэтому распаковка не требуется). При большой нагрузке включение-XX:+UseCompressedStrings
было пессимизацией.[...] Реализация UseCompressedStrings в основном была необязательной функцией, которая поддерживала полностью отличную реализацию
String
вalt-rt.jar
, которая была загружена после поставки опции VM. Дополнительные функции сложнее тестировать, так как они удваивают количество комбинаций опций, чтобы попробовать.
Компактные струны
В Java 9, с другой стороны, компактные строки полностью интегрированы в источник JDK. String
всегда поддерживается byte[]
, где символы используют один байт, если они латинские-1, а два других. Большинство операций выполняют проверку, чтобы увидеть, что происходит, например. charAt
:
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
Компактные строки включены по умолчанию и могут быть частично отключены - "частично", потому что они все еще поддерживаются byte[]
, а возвращаемые операции char
должны все же объединить их из двух отдельных байтов (из-за внутренних свойств это сложно сказать, влияет ли это на производительность).
Подробнее
Если вам интересно узнать больше о компактных строках, я рекомендую прочитать интервью, с которым я связан выше, и/или смотреть этот великий разговор того же Алексея Шипилева (который также объясняет новую конкатенацию строк).
Ответ 2
XX: + UseCompressedStrings и Compact Strings - это разные вещи.
UseCompressedStrings
означает, что строки, которые являются ASCII, могут быть преобразованы в byte[]
, но это было отключено по умолчанию. В jdk-9 эта оптимизация всегда включена, но не через сам флаг, а встраивается.
До тех пор, пока строки java-9 не будут сохранены внутри char[]
в кодировке UTF-16. Из java-9 и вверх они будут храниться как byte[]
. Почему?
Потому что в ISO_LATIN_1
каждый символ может быть закодирован в одном байте (8 бит) против того, к чему он используется до сих пор (16 бит, 8 из которых никогда не использовались). Это работает только для ISO_LATIN_1
, но это большинство используемых строк.
Итак, это делается для использования пространства.
Вот небольшой пример, который должен сделать все более ясным:
class StringCharVsByte {
public static void main(String[] args) {
String first = "first";
String russianFirst = "первыи";
char[] c1 = first.toCharArray();
char[] c2 = russianFirst.toCharArray();
for (char c : c1) {
System.out.println(c >>> 8);
}
for (char c : c2) {
System.out.println(c >>> 8);
}
}
}
В первом случае мы будем получать только нули, что означает, что наиболее значимые 8 бит - это нули; во втором случае будет ненулевое значение, что означает, что присутствует хотя бы один бит из наиболее значимых 8.
Это означает, что если внутри мы храним строки как массив символов, есть строковые литералы, которые фактически тратят половину каждого char. Оказывается, есть несколько приложений, которые на самом деле тратят много места из-за этого.
У вас есть строка из 10 символов Latin1? Вы просто потеряли 80 бит, или 10 байт. Для уменьшения этого сжатия строк было выполнено. И теперь для этих строк не будет потерь пространства.
Внутри это также означает некоторые очень приятные вещи. Чтобы отличить строку LATIN1
и UTF-16
, есть поле coder
:
/**
* The identifier of the encoding used to encode the bytes in
* {@code value}. The supported values in this implementation are
*
* LATIN1
* UTF16
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*/
private final byte coder;
Теперь на основе этого length
вычисляется по-разному:
public int length() {
return value.length >> coder();
}
Если наша строка только Latin1, кодер будет равен нулю, поэтому длина значения (массив байтов) будет размером символов. Для non-Latin1 разделите на два.
Ответ 3
Компактные струны будут иметь лучшее из обоих миров.
Как видно из определения, содержащегося в документации OpenJDK:
Новый класс String будет хранить символы, закодированные либо как ISO-8859-1/Latin-1 (один байт на символ), либо как UTF-16 (два байта на символ), основанный на содержимом строки. Флаг кодирования будет указывать, какая кодировка используется.
Как уже упоминалось @Eugene, большинство строк закодированы в формате Latin-1 и требуют одного байта на символ и, следовательно, не требуют полного 2-байтового пространства в текущей реализации класса String.
Новая реализация класса String будет смещаться от UTF-16 char array
до a byte array
плюс поле флага кодирования. Дополнительное поле кодирования покажет, хранятся ли символы в формате UTF-16 или Latin-1.
Это также завершает, что мы также сможем хранить строки в формате UTF-16, если это необходимо. И это также становится основной точкой различия между сжатой строкой Java 6 и Compact String Java 9, так как в сжатом String используется только массив byte [] для хранения, который затем был представлен как чистый ASCII.
Ответ 4
Сжатые строки (-XX: + UseCompressedStrings)
Это была дополнительная функция, представленная в Java 6 Update 21 для улучшения SPECjbb с помощью кодирования только US- ASCII Строка по байту на символ.
Эта функция может быть включена флагом -XX
(-XX:+UseCompressedStrings
). Когда он включен, String.value
был изменен на ссылку Object и будет указывать либо на byte[]
, для строк, содержащих только 7-разрядные символы US-ASCII, либо a char[]
.
Позже он был удален в Java 7 из-за высокого уровня обслуживания и тестирования.
Компактная строка
Это новая функция, представленная в Java 9, для создания эффективной строки памяти.
Перед Java 9 класс String хранит символы в массиве char, используя два байта для каждого символа, но из Java 9 новый класс String будет хранить символы в byte[]
(один байт на символ) или char[]
(два байта на символ), основанный на содержимом строки, плюс поле флага кодирования. Если строковые символы имеют тип Latin-1
, то byte[]
будет использоваться иначе, если символы имеют тип UTF-16
, тогда будет использоваться char[]
. Флаг кодирования будет указывать, какая кодировка используется.