Czy zastanawialiście się kiedyś jak dużo pamięci zajmują obiekty w Javie? Dużo osób pewnie nigdy się nad tym nie głowiło, a przynajmniej nie przed pierwszym OutOfMemoryError
W tym wpisie postaram się pokazać w jaki sposób oszacować wielkość obiektów, które tworzymy w Javie.
Typy prymitywne
Niewątpliwie najłatwiej jest określić ilość pamięci zajmowanej przez typy prymitywne. Poniżej przedstawiam tabelkę, która pokazuje ile każdy z typów potrzebuję bajtów w pamięci.
boolean | 1 |
byte | 1 |
char | 2 |
int | 4 |
float | 4 |
long | 8 |
double | 8 |
Ilość pamięci potrzebnej do przechowywania booleana może trochę dziwić. W końcu może on przyjąć tylko dwa wartości i w teorii powinien zajmować 1 bit. Specyfikacja JVM nie definiuje ile pamięci jest potrzebne na przechowanie booleana i zależy to od danej implementacji. Dla uproszczenia możemy śmiało przyjąć, iż jest potrzebny 1 bajt.
Reszta wartości wydaję się być w miarę oczywista
Tablice
Każda tablica zajmuje wielokrotność danego typu (co wydaję się oczywiste ;)) oraz dodatkowe 24 bajty, które są narzutem tablicy. Poniżej przedstawiam tabelkę z przykładami, które powinny wszystko wyjaśnić.
byte[] | N + 24 |
char[] | 2N+24 |
int[] | 4N+24 |
double[] | 8N+24 |
Obiekty
I teraz przechodzimy do mięsa tematu – czyli jak oszacować wielkość obiektu naszej klasy.
Aby to zrobić potrzebujemy kilku dodatkowych informacji.
Narzut obiektu
Każdy obiekt, podobnie jak tablica, powoduje dodatkowy narzut pamięci. W tym przypadku jest to 16 bajtów.
Referencja
Każda przetrzymywana referencja w naszym obiekcie to dodatkowe 8 bajtów.
Padding
Wielkość każdego obiektu jest uzupełniana do wielokrotności 8 bajtów.
Praktyka
Jako przykład weźmy klasę String. Poniżej przedstawiam wycinek implementacji tej klasy, który jest potrzebny do oszacowania wielkości obiektu.
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char value[]; private final int offset; private final int count; private int hash; ... }Do obliczenia potrzebujemy zsumować kilka rzeczy:
- Narzut obiektu – 16 bajtów
- Referencja do tablicy – 8 bajtów
- Tablica – 2N + narzut tablicy 24 bajty
- Pole offset – 4 bajty
- Pole count – 4 bajty
- Pole hash – 4 bajty
Zsumowując wszystko wychodzi nam 2N + 60 bajtów. W takim przypadku musimy jeszcze pamiętać o paddingu, czyli dodajemy 4 bajty, aby całkowita wielkość obiektu była wielokrotnością 8.
Podsumowanie
Powyżej przedstawiłem ogólne zasady, których możemy użyć, aby oszacować wielkość naszych obiektów w Javie. Dzięki temu możemy lepiej zrozumieć co jest alokowane w pamięci i jaki rozmiar zajmuje.