Java >> Java tutorial >  >> JVM

boolesk og boolesk[] Hukommelseslayout i JVM

1. Oversigt

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

Først vil vi inspicere JVM'en for at se objektstørrelserne. Så vil vi forstå rationalet bag disse størrelser.

2. Opsætning

For at inspicere hukommelseslayoutet af objekter i JVM'et, kommer vi til at bruge Java Object Layout (JOL) i vid udstrækning. Derfor skal vi tilføje jol-kernen afhængighed:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

3. Objektstørrelser

Hvis vi beder JOL om at udskrive VM-detaljerne i form af objektstørrelser:

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

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

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

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

  • Java-referencer bruger 4 bytes, boolesk s/byte s er 1 byte, char s/kort s er 2 bytes, int s/float s er 4 bytes og endelig lange s/dobbelt s er 8 bytes
  • Disse typer bruger 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ærdien tager 1 byte. På samme måde er hver boolean  på et boolsk[]  bruger 1 byte. Justeringsudfyldninger og objektoverskrifter kan dog øge den plads, der forbruges af boolesk  og boolesk[]  som vi vil se senere.

3.1. Ingen komprimerede referencer

Selv hvis vi deaktiverer de komprimerede referencer via -XX:-UseCompressedOops , den boolske størrelse ændres overhovedet ikke :

# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 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, booleans 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 stykker, mens vi opdaterede en anden.

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

For at opsummere er adresserbarhedsproblemer og ordrivning hovedårsagerne til boolesk s er mere end blot én enkelt bit.

4. Almindelige objektpegere (OOP'er)

Nu hvor vi kender boolesk s er 1 byte, lad os overveje denne simple klasse:

class BooleanWrapper {
    private boolean value;
}

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

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

Så vil JOL udskrive hukommelseslayoutet:

 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0    12           (object header)                           N/A
     12     1   boolean BooleanWrapper.value                      N/A
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

Den BooleanWrapper  layout består af:

  • 12 bytes for overskriften, inklusive to mærker ord og én klasse ord. HotSpot JVM bruger mærket ord til at gemme GC-metadata, identitetshashkode og låseinformation. Den bruger også klassen ord til at gemme klassemetadata såsom kontrol af runtime type
  • 1 byte for den faktiske boolean  værdi
  • 3 bytes udfyldning til justeringsformål

Som standard skal objektreferencer justeres med 8 bytes. Derfor tilføjer JVM 3 bytes til 13 bytes header og boolesk  for at gøre det til 16 bytes.

Derfor boolesk  felter kan bruge mere hukommelse på grund af deres feltjustering.

4.1. Brugerdefineret justering

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

OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0    12           (object header)                           N/A
     12     1   boolean BooleanWrapper.value                      N/A
     13    19           (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 19 bytes external = 19 bytes total

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

5. Array OOPs

Lad os se, hvordan JVM'et opstiller en boolesk  array i hukommelsen:

boolean[] value = new boolean[3];
System.out.println(ClassLayout.parseInstance(value).toPrintable());

Dette vil udskrive instanslayoutet som følger:

OFFSET  SIZE      TYPE DESCRIPTION                              
      0     4           (object header)  # mark word
      4     4           (object header)  # mark word
      8     4           (object header)  # klass word
     12     4           (object header)  # array length
     16     3   boolean [Z.<elements>    # [Z means boolean array                        
     19     5           (loss due to the next object alignment)

Ud over to mark ord og én klasse ord,array pointers indeholder yderligere 4 bytes til at gemme deres længder.

Da vores array har tre elementer, er størrelsen af ​​array-elementerne 3 bytes. Men disse 3 bytes vil blive polstret med 5 feltjusteringsbytes for at sikre korrekt justering.

Selvom hver boolesk  element i et array er kun 1 byte, hele arrayet bruger meget mere hukommelse. Med andre ord, vi bør overveje headeren og polstringen overhead, mens vi beregner matrixstørrelsen.

6. Konklusion

I dette hurtige selvstudie så vi det booleske  felter bruger 1 byte. Vi lærte også, at vi skulle overveje header- og polstringsomkostningerne i objektstørrelser.

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

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


Java tag