boolsk og boolsk hukommelseslayout i JVM

1. Oversigt

I denne hurtige artikel skal vi se, hvad der er fodaftryk for en boolsk værdi i JVM under forskellige omstændigheder.

Først inspicerer vi JVM for at se objektstørrelserne. Derefter forstår vi begrundelsen bag disse størrelser.

2. Opsætning

For at inspicere hukommelseslayoutet på objekter i JVM skal vi bruge Java Object Layout (JOL) i udstrakt grad. Derfor er vi nødt til at tilføje jol-core afhængighed:

 org.openjdk.jol jol-core 0.10 

3. Objektstørrelser

Hvis vi beder JOL om at udskrive VM-detaljerne med hensyn til objektstørrelser:

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

Når de komprimerede referencer er aktiveret (standardadfærd), ser vi output:

# Kører 64-bit HotSpot VM. # Brug af komprimeret oop med 3-bit shift. # Brug af komprimeret klass med 3-bit shift. # Objekter er 8 byte justeret. # Feltstørrelser efter type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array-elementstørrelser: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes ]

I de første par linjer kan vi se nogle generelle oplysninger om VM. Derefter lærer vi om objektstørrelser:

  • Java-referencer bruger 4 byte, boolsks /bytes er 1 byte, chars /korts er 2 byte, ints /flydes er 4 bytes, og til sidst, langs /dobbelts er 8 byte
  • Disse typer forbruger den samme mængde hukommelse, selv når vi bruger dem som array-elementer

Så i nærværelse af komprimerede referencer hver boolsk værdi tager 1 byte. Tilsvarende hver boolsk i en boolsk [] bruger 1 byte. Imidlertid kan justeringspolstringer og objektoverskrifter øge den plads, der forbruges af boolsk og boolsk [] som vi vil se senere.

3.1. Ingen komprimerede referencer

Selvom vi deaktiverer de komprimerede referencer via -XX: -UseCompressedOops, den boolske størrelse ændres slet ikke:

# Feltstørrelser efter type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array-elementstørrelser: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes ]

På den anden side tager Java-referencer dobbelt så meget hukommelse.

Så på trods af hvad vi kunne forvente i starten, booleanere bruger 1 byte i stedet for kun 1 bit.

3.2. Ordrivning

I de fleste arkitekturer er der ingen måde at få adgang til en enkelt bit atomisk. Selvom vi ville gøre det, ville vi sandsynligvis ende med at skrive til tilstødende bits, mens vi opdaterede en anden.

Et af designmålene med JVM er at forhindre dette fænomen, kendt som ordrivning. Det vil sige, at i JVM skal hvert felt og array-element være særskilt; opdateringer til et felt eller element må ikke interagere med læsninger eller opdateringer af noget andet felt eller element.

For at resumere er adresserbarhedsproblemer og ordrivning de vigtigste grunde til det boolsks er mere end kun en enkelt bit.

4. Almindelige objektpegere (OOP'er)

Nu hvor vi ved det boolsks er 1 byte, lad os overveje denne enkle klasse:

klasse BooleanWrapper {privat boolsk værdi; }

Hvis vi inspicerer hukommelseslayoutet for denne klasse ved hjælp af JOL:

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

Derefter udskriver JOL hukommelseslayoutet:

 OFFSET STØRRELSE TYPE BESKRIVELSE VÆRDI 0 12 (objektoverskrift) N / A 12 1 boolean BooleanWrapper.værdi N / A 13 3 (tab på grund af den næste objektjustering) Instansstørrelse: 16 bytes Rumtab: 0 bytes internt + 3 bytes eksternt = 3 bytes i alt

Det Boolsk indpakning layout består af:

  • 12 byte til overskriften, inklusive to mærke ord og en klass ord. HotSpot JVM bruger mærke ord for at gemme GC-metadata, identitetshashkode og låseoplysninger. Det bruger også klass ord for at gemme klassemetadata, såsom kontrol af runtime-typen
  • 1 byte for den aktuelle boolsk værdi
  • 3 byte polstring til justeringsformål

Objektreferencer skal som standard justeres med 8 byte. Derfor tilføjer JVM 3 byte til 13 byte på header og boolsk for at gøre det til 16 byte.

Derfor, boolsk felter kan forbruge mere hukommelse på grund af deres feltjustering.

4.1. Brugerdefineret justering

Hvis vi ændrer justeringsværdien til 32 via -XX: ObjectAlignmentInBytes = 32, derefter skifter det samme klasselayout til:

OFFSET STØRRELSE TYPE BESKRIVELSE VÆRDI 0 12 (objektoverskrift) N / A 12 1 boolean BooleanWrapper.værdi N / A 13 19 (tab på grund af den næste objektjustering) Instansstørrelse: 32 bytes Rumtab: 0 bytes internt + 19 bytes eksternt = 19 bytes i alt

Som vist ovenfor tilføjer JVM 19 byte polstring for at gøre objektstørrelsen til et multiplum af 32.

5. Array OOP'er

Lad os se, hvordan JVM indeholder en boolsk array i hukommelse:

boolsk [] værdi = ny boolsk [3]; System.out.println (ClassLayout.parseInstance (værdi) .toPrintable ());

Dette udskriver instanslayoutet som følger:

OFFSET STØRRELSE TYPE BESKRIVELSE 0 4 (objektoverskrift) # markord 4 4 (objektoverskrift) # markord 8 4 (objektoverskrift) # klassord 12 4 (objektoverskrift) # arraylængde 16 3 boolsk [Z. # [Z betyder boolsk matrix 19 5 (tab på grund af den næste objektjustering)

Ud over to mærke ord og en klass ord, array pointers indeholder yderligere 4 byte til at gemme deres længder.

Da vores matrix har tre elementer, er størrelsen på matrixelementerne 3 bytes. Imidlertid, disse 3 byte polstres med 5 feltjusteringsbyte for at sikre korrekt justering.

Selvom hver boolsk element i en matrix er kun 1 byte, hele matrixen bruger meget mere hukommelse. Med andre ord, vi bør overveje header og polstring overhead, mens vi beregner arraystørrelsen.

6. Konklusion

I denne hurtige vejledning så vi det boolsk felter forbruger 1 byte. Vi lærte også, at vi skulle overveje overskriften og polstringsomkostningerne i objektstørrelser.

For en mere detaljeret diskussion anbefales det stærkt at tjekke Oops-sektionen i JVM-kildekoden. Aleksey Shipilëv har også en meget mere dybtgående artikel på dette område.

Som sædvanligt er alle eksemplerne tilgængelige på GitHub.


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