Kompaktné reťazce v prostredí Java 9

1. Prehľad

Struny v Jave sú interne reprezentované a char [] obsahujúce znaky znaku String. A každý char sa skladá z 2 bajtov, pretože Java interne používa UTF-16.

Napríklad ak a String obsahuje slovo v anglickom jazyku, prvých 8 bitov bude 0 pre každého char, keďže znak ASCII je možné reprezentovať pomocou jedného bajtu.

Veľa znakov vyžaduje na svoju reprezentáciu 16 bitov, ale štatisticky väčšina vyžaduje iba 8 bitov - zastúpenie znakov LATIN-1. Existuje teda priestor na zlepšenie spotreby a výkonu pamäte.

Dôležité je tiež to Strings zvyčajne zvyčajne zaberajú veľkú časť haldy priestoru JVM. A pretože sú uložené v JVM, vo väčšine prípadov a String inštancia môže trvať až dvojnásobne priestor to vlastne potrebuje.

V tomto článku si povieme o možnosti komprimovaného reťazca predstavenej v JDK6 a novom kompaktnom reťazci, ktorý bol nedávno predstavený s JDK9. Oba boli navrhnuté tak, aby optimalizovali spotrebu pamäte Strings na JMV.

2. Stlačený String - Java 6

Aktualizácia JDK 6 Update 21 Performance Release predstavila novú možnosť VM:

-XX: + UseCompressedStrings

Keď je táto možnosť povolená, Struny sú uložené ako byte [], namiesto char [] - tým šetrí veľa pamäte. Táto možnosť však bola nakoniec v JDK 7 odstránená, hlavne preto, že mala nejaké neúmyselné dôsledky na výkon.

3. Kompaktný String - Java 9

Java 9 priniesla koncept kompaktu Struny back.

To znamená, že kedykoľvek vytvoríme a String ak sú všetky znaky String možno reprezentovať pomocou bajtu - reprezentácia LATIN-1, použije sa bajtové pole interne taký, že pre jeden znak je daný jeden bajt.

V iných prípadoch, ak ktorýkoľvek znak vyžaduje na jeho reprezentáciu viac ako 8 bitov, všetky znaky sa uložia pomocou dvoch bajtov pre každú z nich - reprezentácia UTF-16.

Takže v podstate vždy, keď je to možné, použije pre každý znak jeden bajt.

Otázkou teraz je - ako bude všetko možné String prevádzkové práce? Ako rozlíši reprezentácie LATIN-1 a UTF-16?

V rámci riešenia tejto otázky sa vo vnútornej implementácii systému EI zavádza ďalšia zmena String. Máme záverečné pole kodér, ktorá uchováva tieto informácie.

3.1. String Implementácia v prostredí Java 9

Doteraz String bol uložený ako a char []:

hodnota súkromného konečného znaku [];

Odteraz to bude byte []:

hodnota súkromného konečného bajtu [];

Premenná kodér:

súkromný konečný bajtový kodér;

Kde kodér môže byť:

statický konečný bajt LATIN1 = 0; statický konečný bajt UTF16 = 1;

Väčšina z String operácie teraz kontrolujú kodéra a odosielajú do konkrétnej implementácie:

public int indexOf (int ch, int fromIndex) {return isLatin1 ()? StringLatin1.indexOf (hodnota, ch, fromIndex): StringUTF16.indexOf (hodnota, ch, fromIndex); } private boolean isLatin1 () {return COMPACT_STRINGS && coder == LATIN1; } 

Všetky informácie, ktoré JVM potrebuje, sú pripravené a dostupné, CompactString Možnosť VM je predvolene povolená. Na jeho deaktiváciu môžeme použiť:

+ XX: -CompactStrings

3.2. Ako kodér Tvorba

V prostredí Java 9 String implementácia triedy, dĺžka sa počíta ako:

public int length () {návratová hodnota.dĺžka >> kodér; }

Ak String obsahuje iba LATIN-1, hodnotu kodér bude 0, takže dĺžka String bude rovnaká ako dĺžka bajtového poľa.

V ostatných prípadoch, ak String je v zastúpení UTF-16, hodnota kodér bude 1, a teda dĺžka bude o polovicu menšia ako skutočné bajtové pole.

Upozorňujeme, že všetky zmeny boli urobené pre Compact Reťazec, sú vo vnútornej implementácii String triedy a sú plne transparentné pre vývojárov používajúcich String.

4. Kompaktný Struny vs. komprimované Struny

V prípade JDK 6 Compressed Struny, hlavným problémom bolo, že String akceptovaný iba konštruktér char [] ako argument. K tomu ešte veľa String operácie záviseli od char [] reprezentácia a nie bajtové pole. Z tohto dôvodu bolo potrebné vykonať veľa rozbalenia, ktoré ovplyvnilo výkon.

Zatiaľ čo v prípade Compact Reťazec, udržiavanie „codera“ navyše v poli môže tiež zvýšiť réžiu. Znížiť náklady na kodér a vybalenie bajts až chars (v prípade reprezentácie UTF-16) sú niektoré z metód integrované a vylepšený bol aj ASM kód generovaný kompilátorom JIT.

Táto zmena mala za následok niekoľko protiintuitívnych výsledkov. LATIN-1 indexOf (reťazec) požaduje vlastnú metódu, zatiaľ čo indexOf (char) nie. V prípade UTF-16 obidve tieto metódy volajú vnútornú metódu. Tento problém sa týka iba LATIN-1 String a budú opravené v budúcich vydaniach.

Teda kompaktný Struny sú lepšie ako stlačené Struny z hľadiska výkonu.

Zistenie množstva pamäte uloženej pomocou zariadenia Compact Struny, boli analyzované rôzne skládky haldy aplikácií Java. Aj keď výsledky veľmi záviseli od konkrétnych aplikácií, celkové vylepšenia boli takmer vždy značné.

4.1. Rozdiel vo výkone

Pozrime sa na veľmi jednoduchý príklad rozdielu vo výkone medzi zapnutím a vypnutím Compact Struny:

long startTime = System.currentTimeMillis (); Zoznam reťazcov = IntStream.rangeClosed (1, 10_000_000) .mapToObj (Integer :: toString) .collect (toList ()); long totalTime = System.currentTimeMillis () - startTime; System.out.println ("Generované" + strings.size () + "reťazce v" + totalTime + "ms."); startTime = System.currentTimeMillis (); Pripojený reťazec = (String) strings.stream () .limit (100_000) .reduce ("", (l, r) -> l.toString () + r.toString ()); totalTime = System.currentTimeMillis () - startTime; System.out.println ("Vytvorený reťazec dĺžky" + appended.length () + "v" + totalTime + "ms.");

Tu tvoríme 10 miliónov Strings a potom ich naivne pripájať. Keď spustíme tento kód (štandardne sú povolené kompaktné reťazce), dostaneme výstup:

Generované 10 000 000 reťazcov za 854 ms. Vytvorený reťazec s dĺžkou 488895 za 5130 ms.

Podobne, ak ho spustíme zakázaním Compact Strings pomocou: -XX: -CompactStrings možnosť je výstup:

Generované 10 000 000 reťazcov za 936 ms. Vytvorený reťazec s dĺžkou 488895 za 9727 ms.

Je zrejmé, že ide o test povrchovej úrovne, ktorý nemôže byť veľmi reprezentatívny - je to iba snímka toho, čo nová možnosť môže urobiť, aby zlepšila výkon v tomto konkrétnom scenári.

5. Záver

V tomto tutoriáli sme videli pokusy o optimalizáciu výkonu a spotreby pamäte na JVM - ukladaním Strings pamäťovo efektívnym spôsobom.

Celý kód je ako vždy k dispozícii na stránkach Github.


$config[zx-auto] not found$config[zx-overlay] not found