logički i logički izgled memorije u JVM-u

1. Pregled

U ovom brzom članku vidjet ćemo koji je otisak a boolean vrijednost u JVM-u u različitim okolnostima.

Prvo ćemo pregledati JVM kako bismo vidjeli veličine predmeta. Tada ćemo razumjeti obrazloženje tih veličina.

2. Postavljanje

Da bismo pregledali raspored memorije objekata u JVM-u, opsežno ćemo koristiti Java Object Layout (JOL). Stoga moramo dodati jol-core ovisnost:

 org.openjdk.jol jol-core 0.10 

3. Veličine predmeta

Ako tražimo od JOL-a da ispiše detalje o VM-u u smislu Veličine predmeta:

System.out.println (VM.current (). Detalji ());

Kada se omoguće komprimirane reference (zadano ponašanje), vidjet ćemo izlaz:

# Pokretanje 64-bitnog HotSpot VM-a. # Korištenje komprimiranog oop-a s 3-bitnim pomakom. # Upotreba komprimiranog klasa s 3-bitnim pomakom. # Objekti su poravnati 8 bajtova. # Veličine polja prema vrsti: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bajtova] # Veličine elemenata niza: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bajtova ]

U prvih nekoliko redaka možemo vidjeti neke opće informacije o VM-u. Nakon toga saznajemo o veličinama predmeta:

  • Java reference troše 4 bajta, booleans /bajts su 1 bajt, ugljens /krataks su 2 bajta, ints /plutatis su 4 bajta, i konačno, dugos /dvostrukos su 8 bajtova
  • Ove vrste troše jednaku količinu memorije čak i kad ih koristimo kao elemente niza

Dakle, u prisutnosti komprimiranih referenci, svaka boolean vrijednost uzima 1 bajt. Slično tome, svaki boolean u logička [] troši 1 bajt. Međutim, podloge za poravnanje i zaglavlja objekata mogu povećati prostor koji zauzima boolean i logička [] kao što ćemo vidjeti kasnije.

3.1. Nema komprimiranih referenci

Čak i ako onemogućimo komprimirane reference putem -XX: -UseCompressedOops, logička veličina se uopće neće promijeniti:

# Veličine polja prema tipu: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bajtova] # Veličine elemenata niza: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bajtova ]

S druge strane, Java reference uzimaju dvostruko više memorije.

Dakle, unatoč onome što bismo mogli očekivati ​​u početku, booleovi troše 1 bajt umjesto samo 1 bit.

3.2. Razdiranje riječi

U većini arhitektura ne postoji način da se jednom bitu pristupi atomskim putem. Čak i da to želimo učiniti, vjerojatno bismo na kraju zapisivali u susjedne bitove dok bismo ažurirali još jedan.

Jedan od dizajnerskih ciljeva JVM-a je spriječiti ovaj fenomen, poznat kao kidanje riječi. Odnosno, u JVM-u bi svako polje i element polja trebali biti različiti; ažuriranja jednog polja ili elementa ne smiju komunicirati s čitanjima ili ažuriranjima bilo kojeg drugog polja ili elementa.

Da rezimiramo, glavni su razlozi problemi s adresiranjem i kidanje riječi booleans više od samo jednog bita.

4. Obični pokazivači na objekte (OOP)

Sad kad znamo booleans su 1 bajt, razmotrimo ovu jednostavnu klasu:

klasa BooleanWrapper {privatna logička vrijednost; }

Ako pregledamo raspored memorije ove klase pomoću JOL-a:

System.out.println (ClassLayout.parseClass (BooleanWrapper.class) .toPrintable ());

Tada će JOL ispisati izgled memorije:

 VRIJEDNOST ZAMJENE VELIČINE OPIS VRIJEDNOST 0 12 (zaglavlje objekta) N / A 12 1 boolean BooleanWrapper.value N / A 13 3 (gubitak zbog sljedećeg poravnanja objekta) Veličina instance: 16 bajta Prostorni gubici: 0 bajta unutarnjih + 3 bajta eksternih = Ukupno 3 bajta

The BooleanWrapper izgled se sastoji od:

  • 12 bajtova za zaglavlje, uključujući dva ocjena riječi i jedan klasa riječ. HotSpot JVM koristi ocjena riječ za pohranu GC metapodataka, hashcode identiteta i podataka o zaključavanju. Također, koristi klasa riječ za pohranu metapodataka klase kao što su provjere vremena izvođenja
  • 1 bajt za stvarni boolean vrijednost
  • 3 bajta popunjavanja radi poravnanja

Prema zadanim postavkama, reference objekta trebaju biti poravnate za 8 bajtova. Stoga JVM dodaje 3 bajta na 13 bajta zaglavlja i boolean kako bi bio 16 bajtova.

Stoga, boolean polja mogu potrošiti više memorije zbog njihovog poravnanja polja.

4.1. Prilagođeno poravnanje

Ako promijenimo vrijednost poravnanja na 32 putem -XX: ObjectAlignmentInBytes = 32, tada se isti izgled klase mijenja u:

VRIJEDNOST ZAMJENE VELIČINE OPIS VRIJEDNOST 0 12 (zaglavlje objekta) N / A 12 1 boolean BooleanWrapper.value N / A 13 19 (gubitak zbog sljedećeg poravnanja objekta) Veličina instance: 32 bajta Prostorni gubici: 0 bajta unutarnjih + 19 bajtova eksternih = Ukupno 19 bajtova

Kao što je gore prikazano, JVM dodaje 19 bajtova popunjavanja kako bi se veličina objekta umnožila na 32.

5. OOP-ovi niza

Pogledajmo kako JVM postavlja a boolean niz u memoriji:

boolean [] vrijednost = novo boolean [3]; System.out.println (ClassLayout.parseInstance (vrijednost) .toPrintable ());

Ovo će ispisati izgled instance na sljedeći način:

OFFSET VELIČINA OPIS 0 4 (zaglavlje objekta) # riječ oznake 4 4 ​​(zaglavlje objekta) # oznaka riječi 8 4 (zaglavlje objekta) # klasa riječ 12 4 (zaglavlje objekta) # dužina polja 16 3 logička vrijednost [Z. # [Z znači logički niz 19 5 (gubitak zbog sljedećeg poravnanja objekta)

Pored dvije ocjena riječi i jedan klasa riječ, pokazivači na niz sadrže dodatnih 4 bajta za pohranu njihove duljine.

Budući da naš niz ima tri elementa, veličina elemenata niza je 3 bajta. Međutim, ova 3 bajta bit će dodana s 5 bajtova za poravnanje polja kako bi se osiguralo pravilno poravnanje.

Iako svaki boolean element u nizu je samo 1 bajt, cijeli niz troši puno više memorije. Drugim riječima, trebali bismo uzeti u obzir zaglavlje i popunjavanje iznad glave prilikom izračunavanja veličine polja.

6. Zaključak

U ovom brzom vodiču vidjeli smo to boolean polja troše 1 bajt. Također, naučili smo da bismo trebali uzeti u obzir općenite troškove zaglavlja i popunjavanja u veličinama predmeta.

Za detaljniju raspravu, toplo se preporučuje provjeriti oops odjeljak izvornog koda JVM-a. Također, Aleksey Shipilëv ima mnogo detaljniji članak o ovom području.

Kao i obično, svi su primjeri dostupni na GitHubu.