Quels sont les avantages des chaînes compactes par rapport aux chaînes compressées dans JDK9?
Les chaînes compressées (Java 6) et les chaînes compactes (Java 9) ont la même motivation (les chaînes sont souvent en latin-1, de sorte que la moitié de l'espace est perdu) et le but recherché (réduire la taille de ces chaînes), mais les implémentations diffèrent beaucoup.
Dans une interview Aleksey Shipilëv (qui était responsable de la mise en oeuvre de la fonctionnalité Java 9) avait ceci à dire à propos des chaînes compressées:
La fonctionnalité UseCompressedStrings était plutôt conservatrice: tout en distinguant les cas
char[]
etbyte[]
et en essayant de compresser lechar[]
enbyte[]
sur la constructionString
, elle effectuait la plupart des opérationsString
surchar[]
, ce qui nécessitait de décompresser leString.
. , où la plupart des chaînes sont compressibles (la compression n’est donc pas gaspillée) et que seul un nombre limité d’opérationsString
connues sont effectuées sur ces chaînes (aucune décompression n’est donc nécessaire). Dans de très nombreuses charges de travail, activer-XX:+UseCompressedStrings
était une pessimisation.[...] L'implémentation UseCompressedStrings était essentiellement une fonctionnalité facultative qui maintenait une implémentation
String
complètement distincte dansalt-rt.jar
, qui était chargée une fois que l'option VM était fournie. Les fonctionnalités en option sont plus difficiles à tester car elles doublent le nombre de combinaisons d'options à essayer.
En revanche, dans Java 9, les chaînes compactes sont entièrement intégrées à la source JDK. String
est always soutenu par byte[]
, où les caractères utilisent un octet s'ils sont Latin-1 et sinon deux. La plupart des opérations vérifient quel est le cas, par exemple. charAt
:
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
Les chaînes compactes sont activées par défaut et peuvent être partiellement désactivées - "partiellement", car elles sont toujours sauvegardées par un byte[]
et les opérations renvoyant char
s doivent toujours être rassemblées à partir de deux octets distincts (il est difficile de dire si cela a une performance impact).
Si vous souhaitez en savoir plus sur les chaînes compactes, je vous recommande de lire l'interview j'ai lié à ce qui précède et/ou de regarder cet excellent discours du même Aleksey Shipilëv (qui explique également la nouvelle concaténation de chaînes). .
XX: + UseCompressedStrings et Compact Strings sont des choses différentes.
UseCompressedStrings
signifie que les chaînes ne contenant que ASCII peuvent être converties en byte[]
, mais cette option est désactivée par défaut. Dans jdk-9, cette optimisation est toujours activée, mais pas via l'indicateur lui-même, mais intégrée.
Jusqu'à ce que les chaînes Java-9 soient stockées en interne sous la forme d'un char[]
au codage UTF-16. À partir de Java-9, ils seront stockés sous le nom byte[]
. Pourquoi?
Parce que dans ISO_LATIN_1
, chaque caractère peut être codé dans un seul octet (8 bits) par rapport à ce qu’il était jusqu’à présent (16 bits, 8 de chaque où jamais utilisé). Cela fonctionne seulement pour ISO_LATIN_1
, mais c’est la majorité des chaînes utilisées de toute façon.
Donc, cela est fait pour l'utilisation de l'espace.
Voici un petit exemple qui devrait rendre les choses plus claires:
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);
}
}
}
Dans le premier cas, nous n'obtiendrons que des zéros, ce qui signifie que les 8 bits les plus significatifs sont des zéros; dans le second cas, il y aura une valeur non nulle, ce qui signifie qu'au moins un bit du plus significatif 8 est présent.
Cela signifie que si en interne nous stockons Strings sous forme de tableau de caractères, il existe des littéraux de chaîne qui gaspillent la moitié de chaque caractère. Il s'avère que de nombreuses applications gaspillent beaucoup d’espace à cause de cela.
Vous avez une chaîne composée de 10 caractères Latin1? Vous venez de perdre 80 bits ou 10 octets. Pour atténuer cette compression de chaîne a été faite. Et maintenant, il n'y aura pas de perte d'espace pour ces chaînes.
En interne, cela signifie aussi de très belles choses. Pour distinguer les chaînes qui sont LATIN1
et UTF-16
, il existe un champ 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;
Maintenant, cette variable length
est calculée différemment:
public int length() {
return value.length >> coder();
}
Si notre chaîne est uniquement latine1, le codeur sera égal à zéro, donc la longueur de la valeur (le tableau d'octets) est la taille des caractères. Pour les non-latino1 diviser par deux.
Compact Strings aura le meilleur des deux mondes.
Comme on peut le voir dans la définition fournie dans la documentation OpenJDK:
La nouvelle classe String stockera les caractères encodés soit comme ISO-8859-1/Latin-1 (un octet par caractère), soit comme UTF-16 (deux octets par caractère), en fonction du contenu de la chaîne. Le drapeau de codage indiquera quel codage est utilisé.
Comme mentionné par @Eugene, la plupart des chaînes sont codées au format Latin-1 et nécessitent un octet par caractère et ne nécessitent donc pas la totalité de l'espace de 2 octets fourni dans l'implémentation actuelle de la classe String.
La nouvelle implémentation de la classe String passera de UTF-16 char array
à a byte array
plus un champ indicateur d'encodage . Le champ de codage} supplémentaire indiquera si les caractères sont stockés au format UTF-16 ou Latin-1.
Ceci conclut également que nous pourrons également stocker des chaînes au format UTF-16 si nécessaire. Et cela devient également le principal point de différence entre les chaînes compressées de Java 6 et chaînes compactes de Java 9 comme dans la chaîne compressée uniquement le tableau d'octets []utilisé pour le stockage qui a ensuite été représenté par pure ASCII}.
Chaînes compressées (-XX: + UseCompressedStrings)
Il s'agissait d'une fonctionnalité facultative introduite dans Java 6 Update 21 pour améliorer SPECjbb performance en codant uniquement la chaîne US-ASCII sur un octet par caractère.
Cette fonctionnalité peut être activée par un indicateur -XX
(-XX:+UseCompressedStrings
). Lorsqu’il est activé, String.value
a été remplacé par une référence à Object et pointerait soit sur byte[]
, pour les chaînes contenant uniquement des caractères US-ASCII 7 bits, soit sur char[]
.
Plus tard, il a été supprimé de Java 7 en raison de la maintenance élevée et de la difficulté à le tester.
Chaîne compacte
Il s'agit d'une nouvelle fonctionnalité introduite dans Java 9 pour créer une chaîne économe en mémoire.
Avant Java 9, la classe String stockait des caractères dans un tableau de caractères, en utilisant deux octets pour chaque caractère mais, à partir de Java 9, la nouvelle classe String stockait les caractères dans byte[]
(un octet par caractère) ou char[]
(deux octets par caractère), en fonction le contenu de la chaîne, plus un champ encoding-flag. Si les caractères de chaîne sont de type Latin-1
, alors byte[]
sera utilisé sinon, si les caractères sont de type UTF-16
, alors char[]
sera utilisé. Le drapeau de codage indiquera quel codage est utilisé.