Zašto je Java 9 promenila osnovnu implementaciju String-a iz char niza u byte niz?
„Erge, nedavno čitam izvorni kod znakovnih nizova u Java 11, primetio sam veliku razliku u odnosu na Java 8.“ Lice Sanmej je bilo puno mladalačkog osmeha😊, slatko je rekla: „Izvorni kod String klase je optimizovan iz char[] u byte[] za čuvanje sadržaja znakovnog niza, zašto se to radi?“
„Čisto, sa char[] na byte[], najvažiji cilj je ušteda memorijskog prostora koji zauzimaju znakovni nizovi. Jedna dodatna prednost smanjenja memorijske zauzetosti je da će se smanjiti i broj GC-ija.“ objasnio sam pomeranjem naočara desnim palcem.
Zašto optimizovati?
Možemo koristiti jmap -histo:live pid | head -n 10 komandu da vidimo statističke informacije o objektima u heap-u, informacije o ClassLoader-u i finalizer redu.
Za moj trenutno pokrenuti Coding More projekat (baziran na Java 8), rezultat je takav.

Gde ima 17638 String objekata, zauzima 423312 bajtova memorije, treće mesto.
Budući da je unutrašnja implementacija Java 8 String-a i dalje char[], stego možemo videti da memorijsko zauzeće na prvom mestu je upravo char niz.
char[] objekata ima 17673, zauzima 1621352 bajtova memorije, prvo mesto.
To jest, vrlo je neophodno optimizovati String radi uštede memorijskog prostora, ako se optimizuje klasa koja se ne koristi toliko često kao String, nema potrebe, zar ne?
Zašto može uštedeti memorijski prostor?
Kao što svi znaju, podaci char tipa u JVM-u zauzimaju dva bajta, i koriste UTF-8 kodiranje, njihov opseg vrednosti je između '\u0000' (0) i '' (65,535) (uključujući).
To jest, korišćenje char[] za predstavljanje String će dovesti do toga da, čak i ako karakteri u String-u mogu biti predstavljeni sa samo jednim bajtom, ipak mora zauzeti dva bajta.
PS: U računaru, jednobajtni karakteri obično se odnose na karaktere koje jedan bajt (8 bitova) može predstaviti, dok dvobajtni karakteri se odnose na karaktere koji trebaju dva bajta (16 bitova) za predstavljanje. Definicije jednobajtnih i dvobajtnih karaktera su relativne, različiti načini kodiranja odgovaraju različitim jednobajtnim i dvobajtnim skupovima karaktera. Uobičajeni jednobajtni skupovi karaktera uključuju ASCII (American Standard Code for Information Interchange), ISO-8859 (Međunarodna organizacija za standardizaciji standard broj 8859), GBK (Kineski interno kod proširena specifikacija), GB2312 (Kineski nacionalni standard, sada je zamenjen GBK-om), latinična slova, brojevi, znakovi interpunkcije, kontrolni karakteri su svi jednobajtni karakteri. Dvobajtni skupovi karaktera uključuju Unicode, UTF-8, GB18030 (Kineski nacionalni standard), kineski, japanski, korejski, latinični prošireni karakteri pripadaju dvobajtnim karakterima.
Naravno, samo promena char[] u byte[] nije dovoljna, treba saradnjati sa načinom kodiranja Latin-1, ovaj način kodiranja koristi jedan bajt za predstavljanje karaktera, stego štedi više prostora nego UTF-8 kodiranje.
Drugo rečeno, za:
String name = "jack";ovakvo, korišćenje Latin-1 kodiranja, zauzimanje 4 bajtova je dovoljno.
Ali za:
String name = "Mali Er";ovakvo, nema šta, može koristiti samo UTF16 za kodiranje.
Za izvorni kod String-a u JDK 9, kako bi se razlikovao način kodiranja, dodao je coder polje za razlikovanje.
/**
* 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;Java će automatski postaviti odgovarajuće kodiranje na osnovu sadržaja znakovnog niza, bilo Latin-1 bilo UTF16.
To jest, sa char[] na byte[], kineski je dva bajta, čisti engleski je jedan bajt, pre toga, kineski je dva bajta, engleski je takođe dva bajta.
Zašto koristiti UTF-16 a ne UTF-8?
U UTF-8, karakteri brojeva 0-127 se predstavljaju sa 1 bajtom, koriste isti kod kao ASCII. Samo karakteri broja 128 i više koriste 2, 3 ili 4 bajta za predstavljanje.
- Ako postoji samo jedan bajt, onda je najviši bit 0;
- Ako ima više bajtova, onda prvom bajtu od najvišeg bita, koliko uzastopnih bitova ima vrednost 1, toliko bajtova se koristi za kodiranje, ostali bajtovi sve počinju sa 10.
Konkretni oblik izražavanja je:
- 0xxxxxxx: jedan bajt;
- 110xxxxx 10xxxxxx: oblik dva-bajtnog kodiranja (početak dve jedinice);
- 1110xxxx 10xxxxxx 10xxxxxx: oblik tri-bajtnog kodiranja (početak tri jedinice);
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx: oblik četiri-bajtnog kodiranja (početak četiri jedinice).
To jest, UTF-8 je promenljive dužine, za String ovakvu klasu koja ima metode slučajnog pristupa, vrlo je nepogodno. Tako zvani slučajni pristup, to jest metode kao charAt, subString, slobodno navedite jedan broj, String mora može dati rezultat. Ako memorijsko zauzeće svakog karaktera u znakovnom nizu nije fiksne dužine, onda prilikom slučajnog pristupa, treba od početka brojiti dužinu svakog karaktera, kako bi se pronašao karakter koji želite.
Sanmej može postaviti pitanje, UTF-16 je takođe promenljive dužine? Jedan karakter možda zauzima 4 bajta?
Zaista, UTF-16 koristi 2 ili 4 bajta za čuvanje karaktera.
- Za karaktere opsega Unicode broja između 0 ~ FFFF, UTF-16 koristi dva bajta za čuvanje.
- Za karaktere opsega Unicode broja između 10000 ~ 10FFFF, UTF-16 koristi četiri bajta za čuvanje, konkretno: svi bitovi broja karaktera se dele u dva dela, viši bitovi se čuvaju u dvo-bajtnom redu između vrednosti D800~DBFF, niži bitovi (ostali bitovi) se čuvaju u dvo-bajtnom redu između vrednosti DC00~DFFF.
Ali u Javi, jedan karakter (char) je 2 bajta, karakteri koji zauzimaju 4 bajta, u Javi se takođe čuvaju sa dva char-a, i razne operacije String-a, sve su na osnovu Java karaktera (char) kao jedinice, charAt uzima koji char po redu, subString uzima koji do kojeg char čine podniz, čak length vraća broj char-ova.
Stego UTF-16 u Java svetu, može se smatrati fiksnim kodiranjem.
Referenca: https://www.zhihu.com/question/447224628
„Dobro, Sanmej, zašto je Java 9 promenila osnovnu implementaciju String-a iz char niza u bajt niz, pričamo do ovoga.“ rekao sam Sanmeji, „Ponekad, čitanje izvornog koda zaista može mnogo rasti, češćijšte zašto, vrlo dobro!“
„Da, bilo koje znanje ako želite dublje da naučite, možete iskopati mnogo.“ Sanmej je rekla, „na primer, danas smo pričali o UTF-16 i UTF-8.“
„Dobro, trebam da odgovorim na pitanja prijatelja sa planete, možeš malo da odmoriš, na primer da slušaš pesme H.O.T, stvarno lepo.“
„Haha, nisam očekivao da ge, si takođe fan H.O.T!“
