boolesches und boolesches Speicherlayout in der JVM

1. Übersicht

In diesem kurzen Artikel werden wir sehen, wie der Fußabdruck eines booleschen Werts in der JVM unter verschiedenen Umständen aussieht.

Zuerst überprüfen wir die JVM, um die Objektgrößen zu ermitteln. Dann werden wir die Gründe für diese Größen verstehen.

2. Setup

Um das Speicherlayout von Objekten in der JVM zu überprüfen, werden wir das Java Object Layout (JOL) ausgiebig verwenden. Daher müssen wir die Jol-Core- Abhängigkeit hinzufügen :

 org.openjdk.jol jol-core 0.10 

3. Objektgrößen

Wenn wir JOL bitten, die VM-Details in Bezug auf Objektgrößen auszudrucken:

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

Wenn die komprimierten Referenzen aktiviert sind (Standardverhalten), wird die Ausgabe angezeigt:

# 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]

In den ersten Zeilen sehen wir einige allgemeine Informationen zur VM. Danach lernen wir die Objektgrößen kennen:

  • Java Referenzen verbrauchen 4 Bytes, boolean s / Byte s sind 1 Byte, char s / kurze s 2 Bytes, int s / float s sind 4 Bytes, und schließlich lang s / Doppel s sind 8 Bytes
  • Diese Typen verbrauchen dieselbe Menge an Speicher, selbst wenn wir sie als Array-Elemente verwenden

Bei komprimierten Referenzen benötigt jeder boolesche Wert 1 Byte. In ähnlicher Weise verbraucht jeder Boolesche Wert in einem Booleschen Wert [] 1 Byte. Ausrichtungsauffüllungen und Objektüberschriften können jedoch den von boolean und boolean [] belegten Speicherplatz erhöhen, wie wir später sehen werden.

3.1. Keine komprimierten Referenzen

Selbst wenn wir die komprimierten Referenzen über -XX: -UseCompressedOops deaktivieren, ändert sich die boolesche Größe überhaupt nicht :

# 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]

Auf der anderen Seite beanspruchen Java-Referenzen doppelt so viel Speicher.

Trotz aller Erwartungen verbrauchen Boolesche Werte 1 Byte statt nur 1 Bit.

3.2. Wort zerreißen

In den meisten Architekturen gibt es keine Möglichkeit, atomar auf ein einzelnes Bit zuzugreifen. Selbst wenn wir dies wollten, würden wir wahrscheinlich in benachbarte Bits schreiben, während wir ein anderes aktualisieren.

Eines der Entwurfsziele der JVM ist es, dieses Phänomen zu verhindern, das als Wortriss bekannt ist . Das heißt, in der JVM sollte jedes Feld und Array-Element unterschiedlich sein. Aktualisierungen eines Feldes oder Elements dürfen nicht mit Lesevorgängen oder Aktualisierungen eines anderen Felds oder Elements interagieren.

Zusammenfassend sind Adressierbarkeitsprobleme und das Zerreißen von Wörtern die Hauptgründe, warum Boolesche Werte mehr als nur ein einzelnes Bit sind.

4. Ordentliche Objektzeiger (OOPs)

Nachdem wir nun wissen, dass Boolesche Werte 1 Byte sind, betrachten wir diese einfache Klasse:

class BooleanWrapper { private boolean value; }

Wenn wir das Speicherlayout dieser Klasse mit JOL untersuchen:

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

Dann druckt JOL das Speicherlayout:

 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

Das BooleanWrapper- Layout besteht aus:

  • 12 Bytes für den Header, einschließlich zwei Markierungswörtern und einem klassischen Wort. Die HotSpot-JVM verwendet das Markierungswort zum Speichern der GC-Metadaten, des Identitäts-Hashcodes und der Sperrinformationen. Außerdem wird das Wort klass verwendet , um Klassenmetadaten wie Laufzeit-Typprüfungen zu speichern
  • 1 Byte für den tatsächlichen Booleschen Wert
  • 3 Bytes Auffüllen für Ausrichtungszwecke

Standardmäßig sollten Objektreferenzen mit 8 Bytes ausgerichtet sein. Daher addiert die JVM 3 Bytes zu 13 Bytes Header und Boolean , um 16 Bytes zu erhalten.

Daher verbrauchen boolesche Felder aufgrund ihrer Feldausrichtung möglicherweise mehr Speicher.

4.1. Benutzerdefinierte Ausrichtung

Wenn wir den Ausrichtungswert über -XX in 32 ändern: ObjectAlignmentInBytes = 32, ändert sich das gleiche Klassenlayout in:

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

Wie oben gezeigt, fügt die JVM 19 Byte Auffüllung hinzu, um die Objektgröße zu einem Vielfachen von 32 zu machen.

5. Array-OOPs

Mal sehen, wie die JVM ein boolesches Array im Speicher anordnet :

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

Dadurch wird das Instanzlayout wie folgt gedruckt:

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. # [Z means boolean array 19 5 (loss due to the next object alignment)

In addition to two mark words and one klass word, array pointers contain an extra 4 bytes to store their lengths.

Since our array has three elements, the size of the array elements is 3 bytes. However, these 3 bytes will be padded by 5 field alignment bytes to ensure proper alignment.

Although each boolean element in an array is just 1 byte, the whole array consumes much more memory. In other words, we should consider the header and padding overhead while computing the array size.

6. Conclusion

In this quick tutorial, we saw that boolean fields are consuming 1 byte. Also, we learned that we should consider the header and padding overheads in object sizes.

Für eine detailliertere Diskussion wird dringend empfohlen, den Abschnitt oops des JVM-Quellcodes zu lesen. Außerdem hat Aleksey Shipilëv einen viel ausführlicheren Artikel in diesem Bereich.

Wie üblich sind alle Beispiele auf GitHub verfügbar.