Разница между компактными строками и сжатыми строками в Java 9
Каковы преимущества компактных строк над сжатыми строками в JDK9?
4 ответа
Сжатые строки (Java 6) и компактные строки (Java 9) имеют одинаковую мотивацию (строки часто являются эффективно Latin-1, поэтому половина пространства теряется) и цель (сделать эти строки маленькими), но реализации сильно различаются.
Сжатые строки
В своем интервью Алексей Шипилев (отвечавший за реализацию функции Java 9) сказал следующее о сжатых строках:
Функция UseCompressedStrings была довольно консервативной: при различении
char[]
а такжеbyte[]
случай, и пытается сжатьchar[]
вbyte[]
наString
строительство, это сделано больше всего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
Они по-прежнему должны собирать их вместе из двух отдельных байтов (из-за внутренних особенностей трудно сказать, оказывает ли это влияние на производительность).
Больше
Если вам интересно больше узнать о компактных струнах, я рекомендую прочитать интервью, на которое я ссылался выше, и / или посмотреть этот замечательный доклад того же Алексея Шипилева (который также объясняет объединение новых строк).
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.
Это означает, что если внутри мы храним Strings как массив символов, то есть строковые литералы, которые на самом деле тратят половину каждого символа. Оказывается, есть несколько приложений, которые на самом деле тратят много места из-за этого.
У вас есть строка из 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, кодер будет равен нулю, поэтому длина значения (байтовый массив) равна размеру символов. Для нелатинцев1 делим на два.
Компактные струны будут иметь лучшее из обоих миров.
Как видно из определения, приведенного в документации 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 и компактной строкой Java 9, поскольку в сжатой строке использовался только массив byte[] для хранения, который затем представлялся как чистый ASCII.
Сжатые строки (-XX:+UseCompressedStrings)
Это была дополнительная функция, представленная в Java 6 Update 21 для улучшения производительности SPECjbb путем кодирования только строки US-ASCII в байте на символ.
Эта функция может быть включена -XX
флаг (-XX:+UseCompressedStrings
). Когда он включен, String.value
был изменен на ссылку на объект и будет указывать либо на byte[]
для строк, содержащих только 7-битные символы US-ASCII, или char[]
,
Позже он был удален в Java 7 из-за высокого уровня обслуживания и сложности в тестировании.
Компактная строка
Это новая функция, представленная в Java 9 для создания эффективной строки памяти.
До Java 9 класс String хранил символы в массиве char, используя два байта для каждого символа, но из Java 9 новый класс String будет хранить символы в byte[]
(один байт на символ) или char[]
(два байта на символ), основанные на содержимом строки, плюс поле флага кодирования. Если строковые символы имеют тип Latin-1
затем byte[]
будет использоваться иначе, если символы имеют тип UTF-16
затем char[]
будет использоваться. Флаг кодирования будет указывать, какая кодировка используется.