booleaanse en booleaanse geheugenlay-out in de JVM

1. Overzicht

In dit korte artikel gaan we zien wat de voetafdruk is van een boolean waarde in de JVM in verschillende omstandigheden.

Eerst inspecteren we de JVM om de objectgroottes te zien. Dan zullen we de grondgedachte achter die maten begrijpen.

2. Installatie

Om de geheugenlay-out van objecten in de JVM te inspecteren, gaan we de Java Object Layout (JOL) uitgebreid gebruiken. Daarom moeten we het jol-core afhankelijkheid:

 org.openjdk.jol jol-core 0.10 

3. Objectgroottes

Als we JOL vragen om de VM-details af te drukken in termen van objectgroottes:

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

Wanneer de gecomprimeerde verwijzingen zijn ingeschakeld (het standaardgedrag), zien we de uitvoer:

# 64-bit HotSpot VM uitvoeren. # Gebruik gecomprimeerde oop met 3-bit shift. # Gebruik gecomprimeerde klass met 3-bit shift. # Objecten zijn 8 bytes uitgelijnd. # Veldgroottes op type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Grootte array-elementen: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes ]

In de eerste paar regels zien we wat algemene informatie over de VM. Daarna leren we over objectgroottes:

  • Java-verwijzingen verbruiken 4 bytes, booleans /bytes zijn 1 byte, chars /korts zijn 2 bytes, ints /vlotters zijn 4 bytes, en tot slot, langs /dubbeles zijn 8 bytes
  • Deze typen verbruiken dezelfde hoeveelheid geheugen, zelfs als we ze als array-elementen gebruiken

Dus in de aanwezigheid van gecomprimeerde verwijzingen, elk boolean waarde duurt 1 byte. Evenzo elk boolean in een boolean [] verbruikt 1 byte. Uitlijningsvullingen en objectkoppen kunnen echter de ruimte die door wordt ingenomen vergroten boolean en boolean [] zoals we later zullen zien.

3.1. Geen gecomprimeerde verwijzingen

Zelfs als we de gecomprimeerde verwijzingen uitschakelen via -XX: -UseCompressedOops, de booleaanse grootte zal helemaal niet veranderen:

# Veldgroottes op type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Grootte array-elementen: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes ]

Aan de andere kant nemen Java-verwijzingen twee keer zoveel geheugen in beslag.

Dus ondanks wat we in eerste instantie zouden verwachten, booleans verbruiken 1 byte in plaats van slechts 1 bit.

3.2. Woord scheuren

In de meeste architectuur is er geen manier om atomair toegang te krijgen tot een enkele bit. Zelfs als we dit zouden willen doen, zouden we waarschijnlijk naar aangrenzende bits schrijven terwijl we een andere update.

Een van de ontwerpdoelen van de JVM is het voorkomen van dit fenomeen, ook wel woordscheuren genoemd. Dat wil zeggen dat in de JVM elk veld en elk array-element verschillend moet zijn; updates van een veld of element mogen geen interactie hebben met leesbewerkingen of updates van een ander veld of element.

Samenvattend zijn problemen met adresseerbaarheid en het scheuren van woorden de belangrijkste redenen waarom booleans zijn meer dan slechts een enkel bit.

4. Gewone Object Pointers (OOP's)

Nu we het weten booleans zijn 1 byte, laten we deze eenvoudige klasse beschouwen:

class BooleanWrapper {privé booleaanse waarde; }

Als we de geheugenlay-out van deze klasse inspecteren met JOL:

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

Vervolgens zal JOL de geheugenlayout afdrukken:

 OFFSET-GROOTTE TYPE BESCHRIJVING WAARDE 0 12 (objectkop) Nvt 12 1 boolean BooleanWrapper.value Nvt 13 3 (verlies als gevolg van de volgende objectuitlijning) Instantiegrootte: 16 bytes Ruimteverlies: 0 bytes intern + 3 bytes extern = 3 bytes totaal

De BooleanWrapper lay-out bestaat uit:

  • 12 bytes voor de header, inclusief twee Mark woorden en een klass woord. De HotSpot JVM gebruikt de Mark word om de GC-metadata, identiteitshashcode en vergrendelingsinformatie op te slaan. Het gebruikt ook de klass woord om metagegevens van klassen op te slaan, zoals controles van het runtime-type
  • 1 byte voor de feitelijke boolean waarde
  • 3 bytes opvulling voor uitlijningsdoeleinden

Standaard moeten objectreferenties worden uitgelijnd met 8 bytes. Daarom voegt de JVM 3 bytes toe aan 13 bytes header en boolean om er 16 bytes van te maken.

Daarom boolean velden kunnen meer geheugen in beslag nemen vanwege hun velduitlijning.

4.1. Aangepaste uitlijning

Als we de uitlijningswaarde wijzigen in 32 via -XX: ObjectAlignmentInBytes = 32, dan verandert dezelfde indeling van de klas in:

OFFSET SIZE TYPE BESCHRIJVING WAARDE 0 12 (objectkop) Nvt 12 1 boolean BooleanWrapper.value Nvt 13 19 (verlies als gevolg van de volgende objectuitlijning) Instantiegrootte: 32 bytes Ruimteverlies: 0 bytes intern + 19 bytes extern = 19 bytes totaal

Zoals hierboven getoond, voegt de JVM 19 bytes opvulling toe om de objectgrootte een veelvoud van 32 te maken.

5. Array OOP's

Laten we eens kijken hoe de JVM een boolean array in geheugen:

boolean [] waarde = nieuwe boolean [3]; System.out.println (ClassLayout.parseInstance (waarde) .toPrintable ());

Hierdoor wordt de instantie-indeling als volgt afgedrukt:

OFFSET SIZE TYPE BESCHRIJVING 0 4 (objectkop) # markeer woord 4 4 (objectkop) # markeer woord 8 4 (objectkop) # klass woord 12 4 (objectkop) # array lengte 16 3 boolean [Z. # [Z betekent booleaanse array 19 5 (verlies als gevolg van de volgende objectuitlijning)

Naast twee Mark woorden en een klass woord, array-aanwijzers bevatten 4 extra bytes om hun lengtes op te slaan.

Omdat onze array uit drie elementen bestaat, is de grootte van de array-elementen 3 bytes. Echter, deze 3 bytes worden opgevuld met 5 velduitlijningsbytes om een ​​goede uitlijning te garanderen.

Hoewel elk boolean element in een array is slechts 1 byte, verbruikt de hele array veel meer geheugen. Met andere woorden, we moeten rekening houden met de overhead van de koptekst en opvulling bij het berekenen van de array-grootte.

6. Conclusie

In deze korte tutorial hebben we dat gezien boolean velden verbruiken 1 byte. We hebben ook geleerd dat we rekening moeten houden met de overheadkosten voor kopteksten en opvulling in objectgroottes.

Voor een meer gedetailleerde discussie, wordt het ten zeerste aanbevolen om de oops-sectie van de JVM-broncode te bekijken. Ook heeft Aleksey Shipilëv een veel diepgaander artikel op dit gebied.

Zoals gewoonlijk zijn alle voorbeelden beschikbaar op GitHub.