Sprievodca po Java String Pool
1. Prehľad
The String objekt je najpoužívanejšou triedou v jazyku Java.
V tomto rýchlom článku sa pozrieme na súbor Java String Pool - oblasť špeciálnej pamäte, kde Struny sú uložené JVM.
2. String Interning
Vďaka nemennosti Struny v Jave môže JVM optimalizovať množstvo pamäte, ktorá im bola pridelená uchovávajúc iba jednu kópiu každého literálu String v bazéne. Tento proces sa nazýva interning.
Keď vytvoríme a String premennej a priradí jej hodnotu, JVM vyhľadá v súbore a String rovnakej hodnoty.
Ak sa nájde, kompilátor Java jednoducho vráti odkaz na svoju adresu pamäte bez pridelenia ďalšej pamäte.
Ak sa nenájde, pridá sa do fondu (internovaný) a jeho referencia sa vráti.
Napíšeme malý test, ktorý to overí:
String constantString1 = "Baeldung"; String constantString2 = "Baeldung"; assertThat (constantString1) .isSameAs (constantString2);
3. Struny Pridelené pomocou konštruktéra
Keď vytvoríme a String cez Nový operátor, kompilátor Java vytvorí nový objekt a uloží ho do haldy vyhradenej pre JVM.
Každý String takto vytvorený bude ukazovať na iný pamäťový región s jeho vlastnou adresou.
Pozrime sa, ako sa to líši od predchádzajúceho prípadu:
String constantString = "Baeldung"; String newString = nový String ("Baeldung"); assertThat (constantString) .isNotSameAs (newString);
4. String Doslovný vs. Reťazcový objekt
Keď vytvoríme a String objekt pomocou Nový() operátor, vždy vytvorí nový objekt v halde pamäte. Na druhej strane, ak vytvoríme objekt pomocou String doslovna syntax napr. „Baeldung“, môže vrátiť existujúci objekt z bazéna String, ak už existuje. V opačnom prípade vytvorí nový objekt String a vloží sa do fondu reťazcov pre ďalšie použitie v budúcnosti.
Na vysokej úrovni sú obidve String predmety, ale hlavný rozdiel vychádza z bodu, ktorý Nový() operátor vytvorí vždy nový String objekt. Tiež, keď vytvoríme a String pomocou doslovného - je internovaný.
To bude oveľa jasnejšie, keď porovnáme dve String objekty vytvorené pomocou String doslovný a Nový operátor:
Prvý reťazec = "Baeldung"; Druhý reťazec = "Baeldung"; System.out.println (prvý == druhý); // Pravda
V tomto príklade String objekty budú mať rovnaký odkaz.
Ďalej vytvoríme dva rôzne objekty pomocou Nový a skontrolujte, či majú rôzne referencie:
Tretí reťazec = nový reťazec („Baeldung“); Štvrtý reťazec = nový reťazec („Baeldung“); System.out.println (tretí == štvrtý); // Falošné
Podobne, keď porovnáme a String doslovne s a String objekt vytvorený pomocou Nový() operátor pomocou operátora == sa vráti nepravda:
Piaty reťazec = "Baeldung"; Šiesty reťazec = nový reťazec („Baeldung“); System.out.println (piaty == šiesty); // Falošné
Všeobecne, mali by sme použiť String doslovný zápis, ak je to možné. Je ľahšie čitateľný a dáva kompilátoru šancu optimalizovať náš kód.
5. Ručné internovanie
Môžeme ručne internovať a String v Java String Pool zavolaním na stážista () metóda na objekte, ktorý chceme internovať.
Ručné vloženie String uloží svoju referenciu do fondu a JVM ich v prípade potreby vráti.
Vytvorme pre to testovací prípad:
String constantString = "internovaný Baeldung"; String newString = nový String ("internovaný Baeldung"); assertThat (constantString) .isNotSameAs (newString); Reťazec internedString = newString.intern (); assertThat (constantString) .isSameAs (internedString);
6. Zber odpadu
Pred programom Java 7, JVM umiestnil Java String Pool do priečinka PermGen priestor, ktorý má pevnú veľkosť - nedá sa za behu rozšíriť a nie je vhodný na zber odpadu.
Riziko internovania Struny v PermGen (namiesto Halda) je to môžeme dostať Nedostatok pamäte chyba z JVM, ak internujeme príliš veľa Struny.
Od Javy 7 a neskôr je Java String Pool uložené v Halda priestor, v ktorom sa zhromažďujú odpadky JVM. Výhodou tohto prístupu je znížené riziko Nedostatok pamäte chyba pretože neodkazy Struny bude odstránený z fondu, čím sa uvoľní pamäť.
7. Výkon a optimalizácia
V Java 6 je jedinou optimalizáciou, ktorú môžeme vykonať, zvýšenie PermGen priestor počas vyvolania programu pomocou MaxPermSize Možnosť JVM:
-XX: MaxPermSize = 1G
V prostredí Java 7 máme podrobnejšie možnosti na preskúmanie a zväčšenie / zmenšenie veľkosti bazéna. Pozrime sa na dve možnosti zobrazenia veľkosti bazénu:
-XX: + PrintFlagsFinal
-XX: + PrintStringTableStatistics
Ak chceme zvýšiť veľkosť bazéna z hľadiska vedier, môžeme použiť StringTableSize Možnosť JVM:
-XX: StringTableSize = 4901
Pred jazykom Java 7u40 bola predvolená veľkosť fondu 1009 segmentov, ale v novších verziách Java bola táto hodnota predmetom niekoľkých zmien. Presnejšie, predvolená veľkosť fondu od Javy 7u40 do Javy 11 bola 60013 a teraz sa zvýšila na 65536.
Upozorňujeme, že zväčšenie veľkosti fondu bude vyžadovať viac pamäte, ale jeho výhodou je skrátenie času potrebného na vloženie súboru Struny do tabuľky.
8. Poznámka o prostredí Java 9
Až do Java 8, Struny boli interne predstavované ako skupina znakov - char [], zakódované v UTF-16, takže každý znak využíva dva bajty pamäte.
V prostredí Java 9 sa poskytuje nová reprezentácia, tzv Kompaktné struny. Tento nový formát zvolí vhodné kódovanie medzi char [] a byte [] v závislosti od uloženého obsahu.
Od nového String zastúpenie použije UTF-16 kódovanie iba v prípade potreby, množstvo halda pamäť bude výrazne nižšia, čo zase spôsobí menej Smetiar réžia na JVM.
9. Záver
V tejto príručke sme si ukázali, ako JVM a kompilátor Java optimalizujú alokáciu pamäte pre String objekty prostredníctvom fondu Java String Pool.
Všetky ukážky kódu použité v článku sú k dispozícii na GitHub.