Vodič za Java String Pool

1. Pregled

The Niz object je najčešće korištena klasa u jeziku Java.

U ovom ćemo kratkom članku istražiti Java String Pool - posebna memorijska regija gdje Žice pohranjuje JVM.

2. String interniranje

Zahvaljujući nepromjenjivosti Žice u Javi JVM može optimizirati količinu memorije koja im je dodijeljena pohranjivanje samo jedne kopije svakog doslovca Niz u bazenu. Taj se proces naziva interniranje.

Kada stvorimo Niz varijabli i dodijeli joj vrijednost, JVM pretražuje spremište za Niz jednake vrijednosti.

Ako ga pronađe, Java kompajler jednostavno će vratiti referencu na svoju memorijsku adresu, bez dodjeljivanja dodatne memorije.

Ako nije pronađena, bit će dodana u spremište (internirana) i vraćena će biti njena referenca.

Napišimo mali test da to provjerimo:

String constantString1 = "Baeldung"; String constantString2 = "Baeldung"; assertThat (constantString1) .isSameAs (constantString2);

3. Žice Dodijeljeno pomoću konstruktora

Kada stvorimo a Niz putem novi operatora, Java kompajler stvorit će novi objekt i pohraniti ga u prostor hrpe rezerviran za JVM.

Svaki Niz ovako stvoreno ukazat će na drugu memorijsku regiju s vlastitom adresom.

Pogledajmo kako se ovo razlikuje od prethodnog slučaja:

String constantString = "Baeldung"; String newString = novi niz ("Baeldung"); assertThat (constantString) .isNotSameAs (newString);

4. Niz Doslovno vs. String objekt

Kada stvorimo a Niz objekt pomoću novi() operatora, uvijek stvara novi objekt u memoriji hrpe. S druge strane, ako stvorimo objekt pomoću Niz doslovna sintaksa na pr. "Baeldung", on može vratiti postojeći objekt iz spremišta nizova, ako već postoji. Inače će stvoriti novi objekt String i staviti ga u spremište nizova za buduću ponovnu upotrebu.

Na visokoj su razini obje Niz predmeta, ali glavna razlika dolazi iz točke koja novi() operator uvijek stvara novi Niz objekt. Također, kada kreiramo Niz koristeći doslovno - internira se.

To će biti puno jasnije kada usporedimo dva Niz objekti stvoreni pomoću Niz doslovno i novi operater:

Niz prvi = "Baeldung"; Niz drugi = "Baeldung"; System.out.println (prva == druga); // Istina

U ovom primjeru, Niz objekti će imati istu referencu.

Dalje, kreirajmo dva različita objekta pomoću novi i provjerite imaju li različite reference:

Treći niz = novi niz ("Baeldung"); Niz četvrti = novi niz ("Baeldung"); System.out.println (treći == četvrti); // Lažno

Slično tome, kada uspoređujemo a Niz doslovno s a Niz objekt stvoren pomoću novi() operator koji koristi == operator, vratit će se lažno:

Niz peti = "Baeldung"; Niz šesti = novi niz ("Baeldung"); System.out.println (peti == šesti); // Lažno

Općenito, trebali bismo koristiti Niz doslovni zapis kad je to moguće. Jednostavnije je za čitanje i daje prevoditelju priliku da optimizira naš kod.

5. Ručno interniranje

Možemo ručno internirati a Niz u Java String Poolu pozivanjem datoteke pripravnik () metoda na objektu kojeg želimo internirati.

Ručno interniranje Niz pohranit će svoju referencu u spremište, a JVM će je vratiti po potrebi.

Stvorimo test slučaja za ovo:

String constantString = "internirani Baeldung"; String newString = novi niz ("internirani Baeldung"); assertThat (constantString) .isNotSameAs (newString); Niz internedString = newString.intern (); assertThat (constantString) .isSameAs (internedString);

6. Odvoz smeća

Prije Jave 7, JVM smjestio Java String Pool u PermGen prostora koji ima fiksnu veličinu - ne može se proširiti za vrijeme izvođenja i ne ispunjava uvjete za odvoz smeća.

Rizik stažiranja Žice u PermGen (umjesto Hrpa) je li to možemo dobiti Bez memorije pogreška iz JVM-a ako ih interniramo previše Žice.

Od Jave 7 nadalje, Java String Pool je pohranjeni u Hrpa prostor, koji se sakuplja smeće od strane JVM-a. Prednost ovog pristupa je smanjeni rizik od Bez memorije pogreška jer bez reference Žice bit će uklonjen iz spremišta, čime će se osloboditi memorija.

7. Izvedba i optimizacije

U Javi 6, jedina optimizacija koju možemo izvesti je povećanje PermGen prostora tijekom poziva programa pomoću MaxPermSize JVM opcija:

-XX: MaxPermSize = 1G

U Javi 7 imamo detaljnije mogućnosti za ispitivanje i proširivanje / smanjenje veličine bazena. Pogledajmo dvije mogućnosti za pregled veličine bazena:

-XX: + PrintFlagsFinal
-XX: + PrintStringTableStatistics

Ako želimo povećati veličinu bazena u smislu segmenata, možemo koristiti StringTableSize JVM opcija:

-XX: StringTableSize = 4901

Prije Jave 7u40, zadana veličina spremišta bila je 1009 segmenata, ali ova je vrijednost podložna nekoliko promjena u novijim verzijama Jave. Točnije, zadana veličina spremišta od Jave 7u40 do Jave 11 bila je 60013, a sada se povećala na 65536.

Imajte na umu da će povećanje veličine spremišta potrošiti više memorije, ali prednost mu je što smanjuje vrijeme potrebno za umetanje Žice u stol.

8. Napomena o Javi 9

Do Jave 8, Žice bili su interno predstavljeni kao niz znakova - char [], kodirano u UTF-16, tako da svaki lik koristi dva bajta memorije.

S Javom 9 pruža se novi prikaz, tzv Kompaktne žice. Ovaj novi format odabrat će odgovarajuće kodiranje između char [] i bajt[] ovisno o pohranjenom sadržaju.

Od novog Niz zastupanje će koristiti UTF-16 kodiranje samo kada je potrebno, količina od hrpa memorija će biti znatno niža, što zauzvrat uzrokuje manje Sakupljač smeća iznad glave na JVM.

9. Zaključak

U ovom smo vodiču pokazali kako JVM i Java kompajler optimiziraju dodjelu memorije za Niz objekata putem Java String Pool-a.

Svi uzorci koda korišteni u članku dostupni su na GitHubu.