Verschil tussen compacte strings en gecomprimeerde strings in Java 9

Wat zijn de voordelen van compacte stringsten opzichte van gecomprimeerde strings in JDK9?


Antwoord 1, autoriteit 100%

Gecomprimeerde strings (Java 6) en compacte strings (Java 9) hebben beide dezelfde motivatie (strings zijn vaak effectief Latin-1, dus de helft van de ruimte wordt verspild) en doel (maak die strings klein), maar de implementaties verschillen een veel.

Gecomprimeerde snaren

In een interviewAleksey Shipilëv (die verantwoordelijk voor de implementatie van de Java 9-functie) had dit te zeggen over gecomprimeerde strings:

UseCompressedStrings-functie was nogal conservatief: terwijl onderscheid werd gemaakt tussen char[]en byte[]hoofdletters, en probeerde de char[]te comprimeren in byte[]op Stringconstructie, deed het de meeste Stringbewerkingen op char[], die nodig waren om uit te pakken de String.Daarom profiteerde het alleen van een speciaal type werkbelasting, waar de meeste strings comprimeerbaar zijn (zodat compressie niet verloren gaat), en slechts een beperkte hoeveelheid bekende Stringer bewerkingen op worden uitgevoerd (dus uitpakken is niet nodig). Bij veel workloads was het inschakelen van -XX:+UseCompressedStringseen pessimisering.

[…] UseCompressedStrings-implementatie was in feite een optionele functie die een volledig verschillende String-implementatie in alt-rt.jarhandhaafde, die werd geladen zodra de VM-optie is aangeleverd. Optionele functies zijn moeilijker te testen, omdat ze het aantal te proberen optiecombinaties verdubbelen.

Compacte snaren

In Java 9 daarentegen zijn compacte strings volledig geïntegreerd in de JDK-bron. Stringwordt altijdondersteund door byte[], waarbij tekens één byte gebruiken als ze Latin-1 zijn en anders twee. De meeste bewerkingen doen een controle om te zien wat het geval is, b.v. charAt:

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

Compacte tekenreeksen zijn standaard ingeschakeld en kunnen gedeeltelijk worden uitgeschakeld – “gedeeltelijk” omdat ze nog steeds worden ondersteund door een byte[]en bewerkingen die chars retourneren, moeten nog steeds worden geplaatst ze samen uit twee afzonderlijke bytes (vanwege de intrinsieke waarde is het moeilijk te zeggen of dit invloed heeft op de prestaties).

Meer

Als je meer achtergrondinformatie over compacte strings wilt, raad ik aan om het interviewwaarnaar ik hierboven heb gelinkt en/of bekijk deze geweldige toespraak van dezelfde Aleksey Shipilëv(wat ook de nieuwe tekenreeksaaneenschakeling verklaart).


Antwoord 2, autoriteit 36%

XX:+UseCompressedStringsen Compact Stringszijn verschillende dingen.

UseCompressedStringsbetekende dat tekenreeksen die alleen ASCII zijn, konden worden geconverteerd naar byte[], maar dit was standaard uitgeschakeld. In jdk-9 staat deze optimalisatie altijd aan, maar niet via de vlag zelf, maar ingebouwd.

Tot java-9 Strings intern worden opgeslagen als een char[]in UTF-16-codering. Vanaf java-9 en hoger worden ze opgeslagen als byte[]. Waarom?

Omdat in ISO_LATIN_1elk teken kan worden gecodeerd in een enkele byte (8 bits) versus wat het tot nu toe was (16 bits, waarvan 8 nooit gebruikt). Dit werkt alleenvoor ISO_LATIN_1, maar dat is hoe dan ook het merendeel van de strings die worden gebruikt.

Dus dat is gedaan voor ruimtegebruik.

Hier is een klein voorbeeld dat de zaken duidelijker moet maken:

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

In het eerste geval krijgen we alleen nullen, wat betekent dat de meest significante 8 bits nullen zijn; in het tweede geval is er een waarde die niet nul is, wat betekent dat er minstens één bit van de meest significante 8 aanwezig is.

Dat betekent dat als we Strings intern opslaan als een array van tekens, er letterlijke tekenreeksen zijn die in feite de helft van elk teken verspillen. Het blijkt dat er meerdere applicaties zijn die hierdoor veel ruimte verspillen.

Je hebt een string gemaakt van 10 Latin1-tekens? Je bent zojuist 80 bits of 10 bytes kwijt. Om dit te verminderen is String-compressie gemaakt. En nu zal er geen ruimteverlies zijn voor deze Strings.

Intern betekent dit ook een aantal hele leuke dingen. Om onderscheid te maken tussen String die LATIN1en UTF-16zijn, is er een veld 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;

Nu gebaseerd op deze lengthwordt anders berekend:

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

Als onze String alleen Latin1 is, wordt de coder nul, dus de lengte van de waarde (de byte-array) is de grootte van tekens. Voor niet-Latijns1 delen door twee.


Antwoord 3, autoriteit 9%

Compact Stringsheeft het beste van twee werelden.

Zoals te zien is in de definitie in de OpenJDK-documentatie:

De nieuwe klasse String zal tekens opslaan die zijn gecodeerd als ISO-8859-1/Latin-1 (één byte per teken) of als UTF-16 (twee bytes per teken), op basis van de inhoud van de tekenreeks. De coderingsvlag geeft aan welke codering wordt gebruikt.

Zoals vermeld door @Eugene, zijn de meeste tekenreeksen gecodeerd in Latin-1-indeling en vereisen ze één byte per teken en vereisen daarom niet de volledige 2-byte-ruimte in de huidige implementatie van de String-klasse.

De nieuwe String class-implementatie zal verschuiven van UTF-16 char arraynaar a byte arrayplus een encoding-flag-veld. Het extra coderingsveldlaat zien of de tekens zijn opgeslagen in UTF-16- of Latin-1-indeling.

Hieruit wordt ook geconcludeerd dat we indien nodig ook strings in UTF-16-indeling kunnen opslaan. En dit wordt ook het belangrijkste verschilpunt tussen de Compressed String of Java 6en Compact String of Java 9zoals in Compressed String only byte[] arrayem>werd gebruikt voor opslag die vervolgens werd weergegeven als pure ASCII.


Antwoord 4

Compressed Strings(-XX:+UseCompressedStrings)

Dit was een optionele functie die werd geïntroduceerd in Java 6Update 21 om SPECjbbprestaties door alleen US-ASCII-tekenreeks te coderen op een byte per teken.

Deze functie kan worden ingeschakeld door een -XX-vlag (-XX:+UseCompressedStrings). Als het is ingeschakeld, is String.valuegewijzigd in een objectreferentie en zou het verwijzen naar een byte[], voor tekenreeksen die alleen 7-bits US-ASCII-tekens bevatten, of anders een char[].

Later werd het verwijderd in Java 7 vanwege veel onderhoud en moeilijk te testen.

Compacte tekenreeks

Dit is een nieuwe functie die is geïntroduceerd in Java 9om een ​​geheugenefficiënte string te bouwen.

Vóór Java 9 bewaarde de String-klasse tekens in een char-array, met twee bytes voor elk teken, maar vanaf Java 9 slaat de nieuwe String-klasse tekens op in byte[](één byte per teken ) of char[](twee bytes per teken), gebaseerd op de inhoud van de tekenreeks, plus een coderingsvlagveld. Als tekenreeksen van het type Latin-1zijn, wordt byte[]anders gebruikt als tekens van het type UTF-16zijn en dan char[]zal worden gebruikt. De coderingsvlag geeft aan welke codering wordt gebruikt.

Other episodes