it-swarm.com.de

Unterschied zwischen kompakten Strings und komprimierten Strings in Java 9

Welche Vorteile bieten compact-Strings gegenüber komprimierten Strings in JDK9?

64
anonymous

Komprimierte Strings (Java 6) und kompakte Strings (Java 9) haben beide die gleiche Motivation (Strings sind oft effektiv Latin-1, daher wird nur die Hälfte des Platzes verschwendet) und Ziel (machen diese Strings klein), aber die Implementierungen unterscheiden sich erheblich.

Komprimierte Zeichenfolgen

In ein Interview Aleksey Shipilëv (der mit der Implementierung der Java 9-Funktion beauftragt war) hatte Folgendes zu komprimierten Strings zu sagen:

Die UseCompressedStrings-Funktion war eher konservativ: Bei der Unterscheidung zwischen char[] und byte[] case und dem Versuch, char[] in byte[] auf String zu komprimieren, wurden die meisten String-Vorgänge für char[] ausgeführt, die zum Auspacken des String.-Vorgangs erforderlich waren , wo die meisten Zeichenfolgen komprimierbar sind (die Komprimierung geht also nicht verloren) und nur eine begrenzte Anzahl bekannter String-Vorgänge wird für sie ausgeführt (daher ist kein Auspacken erforderlich). In vielen Workloads war die Aktivierung von -XX:+UseCompressedStrings eine Pessimisierung.

[...] Die UseCompressedStrings-Implementierung war im Grunde eine optionale Funktion, die eine vollständig unterschiedliche String-Implementierung in alt-rt.jar unterhielt, die geladen wurde, sobald die Option VM bereitgestellt wurde. Optionale Funktionen sind schwieriger zu testen, da sie die Anzahl der zu testenden Optionskombinationen verdoppeln.

Kompakte Saiten

In Java 9 hingegen sind kompakte Zeichenfolgen vollständig in die JDK-Quelle integriert. String ist always und wird von byte[] unterstützt, wobei Zeichen ein Byte verwenden, wenn sie Latin-1 und andernfalls zwei sind. Die meisten Operationen prüfen, um zu sehen, was der Fall ist, z. charAt:

public char charAt(int index) {
    if (isLatin1()) {
        return StringLatin1.charAt(value, index);
    } else {
        return StringUTF16.charAt(value, index);
    }
}

Kompakte Zeichenfolgen sind standardmäßig aktiviert und können teilweise deaktiviert werden - "teilweise", weil sie immer noch durch einen byte[] gesichert sind, und Operationen, die chars zurückgeben, müssen sie immer noch aus zwei separaten Bytes zusammenstellen (aufgrund von Intrinsics ist es schwer zu sagen, ob dies eine Performance hat Auswirkung).

Mehr

Wenn Sie an mehr Hintergrundwissen zu kompakten Saiten interessiert sind, empfehle ich, das Interview zu lesen, das ich oben verlinkt habe, und/oder zuzusehen dieses großartige Gespräch von Aleksey Shipilëv (was auch die neue String-Verkettung erklärt) .

70
Nicolai

XX: + UseCompressedStrings und Compact Strings sind verschiedene Dinge. 

UseCompressedStrings bedeutete, dass nur Strings, die ASCII sind, in byte[] konvertiert werden konnten, dies war jedoch standardmäßig deaktiviert. In jdk-9 ist diese Optimierung immer aktiviert, aber nicht über das Flag selbst, sondern eingebaut. 

Bis Java-9-Strings werden intern als char[] in UTF-16-Kodierung gespeichert. Ab Java-9 werden sie als byte[] gespeichert. Warum?

Weil in ISO_LATIN_1 jedes Zeichen in einem einzigen Byte (8 Bit) gegenüber dem, was es bisher war, codiert werden kann (16 Bit, von denen 8 jeweils nie verwendet wurden). Dies funktioniert nur für ISO_LATIN_1, aber das ist die Mehrheit der verwendeten Strings.

Das wird also aus Platzgründen getan. 

Hier ein kleines Beispiel, das die Dinge klarer machen soll:

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);
        }
    }
}

Im ersten Fall erhalten wir nur Nullen, was bedeutet, dass die höchstwertigen 8 Bits Nullen sind. Im zweiten Fall wird es einen Wert ungleich Null geben, was bedeutet, dass mindestens ein Bit von der höchstwertigen Zahl 8 vorhanden ist.

Das heißt, wenn wir Strings intern als ein Array von Zeichen speichern, gibt es String-Literale, die tatsächlich die Hälfte jedes Zeichens verschwenden. Es stellt sich heraus, dass es mehrere Anwendungen gibt, die dadurch viel Platz verschwenden.

Sie haben einen String aus 10 Latin1-Zeichen? Sie haben gerade 80 Bits oder 10 Bytes verloren. Um dies zu mildern, wurde eine String-Komprimierung vorgenommen. Und jetzt gibt es keinen Platzverlust für diese Saiten. 

Intern bedeutet das auch ein paar sehr schöne Dinge. Um zwischen Zeichenketten, die LATIN1 und UTF-16 sind, zu unterscheiden, gibt es ein Feld 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;

Basierend auf dieser length wird nun anders berechnet:

public int length() {
    return value.length >> coder();
}

Wenn unser String nur Latin1 ist, wird der Codierer gleich Null sein. Die Wertlänge (das Byte-Array) ist also die Zeichengröße. Bei Nicht-Latin1 dividiere durch zwei.

23
Eugene

Compact Strings wird das Beste aus beiden Welten haben.

Wie Sie der Definition in der OpenJDK-Dokumentation entnehmen können: 

Die neue String-Klasse speichert Zeichen, die entweder als ISO-8859-1/Latin-1 (ein Byte pro Zeichen) oder als UTF-16 (zwei Byte pro Zeichen) codiert sind, basierend auf dem Inhalt der Zeichenfolge. Das Codierungsflag zeigt an, welche Codierung verwendet wird.

Wie von @Eugene erwähnt, sind die meisten Zeichenfolgen im Latin-1-Format codiert und erfordern ein Byte pro Zeichen und benötigen daher nicht den gesamten 2-Byte-Speicherplatz, der in der aktuellen Implementierung der String-Klasse bereitgestellt wird.

Die neue Implementierung der String-Klasse wechselt von UTF-16 char array zu a byte array plus ein Codierungsflag-Feld . Das zusätzliche Kodierungsfeld zeigt an, ob die Zeichen im UTF-16- oder Latin-1-Format gespeichert werden.

Daraus schließt sich auch, dass wir bei Bedarf auch Strings im UTF-16-Format speichern können. Und dies ist auch der Hauptunterschied zwischen Compressed String von Java 6 und Compact String von Java 9 wie in Compressed String nur Byte [] Array wird für die Speicherung verwendet, die dann als pure ASCII} dargestellt wird. 

7
Dhaval Simaria

Compressed Strings (-XX: + UseCompressedStrings)

Dies war eine optionale Funktion, die in Java 6 Update 21 eingeführt wurde, um die Leistung von SPECjbb zu verbessern, indem nur US-ASCII-Zeichenfolgen für ein Byte pro Zeichen codiert werden.

Diese Funktion kann durch ein -XX-Flag (-XX:+UseCompressedStrings) aktiviert werden. Wenn es aktiviert ist, wurde String.value in eine Objektreferenz geändert und würde entweder auf einen byte[] für Zeichenfolgen verweisen, die nur 7-Bit-US-ASCII-Zeichen enthalten, oder auf einen char[]

Später wurde es in Java 7 entfernt, da es sehr wartungsaufwändig und schwer zu testen ist.

Compact String

Dies ist eine neue Funktion, die in Java 9 eingeführt wurde, um einen speichereffizienten String zu erstellen. 

Vor Java 9 wurden in der String-Klasse Zeichen in einem Char-Array gespeichert, wobei zwei Byte für jedes Zeichen verwendet wurden. In Java 9 werden jedoch in der neuen String-Klasse Zeichen in byte[] (ein Byte pro Zeichen) oder char[] (zwei Byte pro Zeichen) gespeichert den Inhalt der Zeichenfolge sowie ein Codierungsflag-Feld. Wenn Zeichenfolgen vom Typ Latin-1 sind, wird byte[] verwendet. Wenn Zeichen vom Typ UTF-16 sind, wird char[] verwendet. Das Codierungsflag zeigt an, welche Codierung verwendet wird.

0
Mohit Tyagi