Java Object Layout – czyli jak sprawdzić rozmiar obiektów w pamięci

Java Object Layout – czyli jak sprawdzić rozmiar obiektów w pamięci

Java Object Layout to biblioteka która umożliwia sprawdzenie ile miejsca w pamięci zajmuje utworzony obiekt:

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

Zanim zaczniemy weryfikować ile pamięci zajmują obiekty w Javie to należy doprecyzować pewne kwestie. Programy napisane w Javie mogą być uruchomione na dowolnej platformie. Z tego też powodu wirtualna maszyna Javy posiada predefiniowane rozmiary dla typów prymitywnych:

byte 1 byte Stores whole numbers from -128 to 127
short 2 bytes Stores whole numbers from -32,768 to 32,767
int 4 bytes Stores whole numbers from -2,147,483,648 to 2,147,483,647
long 8 bytes Stores whole numbers from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
float 4 bytes Stores fractional numbers. Sufficient for storing 6 to 7 decimal digits
double 8 bytes Stores fractional numbers. Sufficient for storing 15 decimal digits
boolean 1 bit Stores true or false values
char 2 bytes Stores a single character/letter or ASCII values

Każdy typ złożony przechowuje informacje o samym sobie tzw. metadane:

class (4 bajty) – wskaźnik do typu klasy np. dla obiektu java.lang.Integer jest to wskaźnik do klasy java.lang.Integer,

flagi (4 bajty) – informacja czy zdefiniowany typ to klasa czy tablica oraz informacja o hashCode,

lock (4 bajty) – monitor czyli informacja czy dostęp do obiektu jest synchronizowany,

size (4 bajty) – rozmiar tablicy – występuje tylko i wyłącznie dla tablic.

Dla klasy java.lang.Integer i 32 bitowego JVMa informacje te można przedstawić następująco:

Example layout of a java.lang.Integer object for a 32-bit Java process

Zdefiniujmy dla przykładu prostą klasę Shortcut:

public class Shortcut {
    int shortcut;
    public static void main(String[] args) {
        System.out.println(VM.current().details());
    }
}

po uruchomieniu programu w wyniku otrzymamy informacje o użytej wersji maszyny wirtualnej:

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

wyświetlmy teraz informacje o rozmiarze utworzonego obiektu klasy Shortcut:

 System.out.println(ClassLayout.parseClass(Shortcut.class).toPrintable(new Shortcut()));

wynik:

Shortcut object internals:
 OFFSET  SIZE   TYPE DESCRIPTION     VALUE
      0     4  (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4  (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4  (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4   int Shortcut.shortcut 0
Instance size: 16 bytes

otrzymujemy 16 bajtów – (4 bajty zmienna shortcut, 12 bajtów metadane klasy). Dodajmy teraz zmienną shortcutDescription:

public class Shortcut {
    int shortcut;
    String shortcutDescription;
 
    public static void main(String[] args) {
        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(Shortcut.class).toPrintable(new Shortcut()));
    }
}

wynik to 24 bajty a nie jak można było się spodziewać 20 (referencja do obiektu klasy String zajmuje 4 bajty)!

Shortcut object internals:
 OFFSET  SIZE  TYPE DESCRIPTION VALUE
      0     4  (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4  (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4  (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4  int Shortcut.shortcut 0
     16     4   java.lang.String Shortcut.shortcutDescription null
     20     4  (loss due to the next object alignment)
Instance size: 24 bytes

Dlaczego jest taka różnica? Wynika to z tego, że wystąpiło wyrównanie do 8 bajtów. Najbliższej 20 jest liczba 24 biorąc pod uwagę wielokrotność liczby 8. Wadą klasy ClassLayout jest to, że nie zlicza ona rozmiaru dla typów zagnieżdżonych. Problem ten rozwiązać można z użyciem klasy GraphLayout.

 

Leave a comment

Your email address will not be published.


*