Cookies op Tweakers

Tweakers is onderdeel van DPG Media en maakt gebruik van cookies, JavaScript en vergelijkbare technologie om je onder andere een optimale gebruikerservaring te bieden. Ook kan Tweakers hierdoor het gedrag van bezoekers vastleggen en analyseren. Door gebruik te maken van deze website, of door op 'Cookies accepteren' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt? Bekijk dan ons cookiebeleid.

Meer informatie

Door Arjen van der Meijden

Lead Developer

Praktisch geheugenbeheer in Java bij Tweakers.net

Objecten in Java

Gegevens worden in Java met objecten beschreven. In deze objecten kunnen primitieven - zoals getallen, booleans en karakters - worden opgeslagen, ze kunnen naar andere objecten verwijzen en ze kunnen een combinatie van primitieven en verwijzingen bevatten.

Al die zaken kosten uiteraard geheugen. De precieze hoeveelheid hangt af van de gebruikte Java Virtual Machine. Er is bijvoorbeeld geen garantie dat de IBM JDK of de JRockit VM evenveel geheugen voor een object gebruikt als de Oracle JDK, hoewel er in de praktijk waarschijnlijk veel overeenkomsten zijn.

Met de hotspot-compiler van Oracle wordt voor elk object een header van twee referenties gebruikt, waaraan vervolgens de gewenste data wordt toegevoegd. Een referentie beslaat 4 bytes bij 32bits-systemen en 8 bytes bij een 64bits-VM. Met CompressedOops kunnen referenties bij een 64bits-VM in slechts 4 bytes worden gecodeerd. Aangezien CompressedOops sinds Oracles Java 7 standaard is ingeschakeld, gaan we bij de onderstaande berekeningen uit van referenties van 4 bytes.

De exacte geheugenlay-out van objecten in Oracles JVM is helaas niet eenvoudig te achterhalen en wordt ook niet gespecificeerd in de Java Language Specification. Daarin is alleen vastgelegd welke waarden moeten kunnen worden opgeslagen in een byte, short, int, float, long, double of char. Een boolean kost in de Oracle JVM 1 byte, maar in een andere JVM zouden bijvoorbeeld 8 booleans in 1 byte gegroepeerd kunnen worden.

Primitieve
BitsBytes
byte 8 1
short 16 2
int en float 32 4
long en double 64 8
char 16 2
boolean 1 - 8 1
referentie 32 of 64 4 of 8

De Oracle JVM slaat objecten in blokken van 8 bytes op, omdat de toegang en het beheer van de data zo efficiënter zijn dan wanneer met losse bytes wordt gewerkt. Omdat 8 bytes net genoeg is om de twee verplichte referenties in op te slaan, beslaat een object met een willekeurig veld dus altijd minimaal 16 bytes. Als objecten kleiner zijn dan een veelvoud van 8 bytes, blijft er altijd wat loze ruimte over, die 'padding' wordt genoemd.

Objectgroottes

Een bekend object in Java is de string, waarin een tekenreeks kan worden opgeslagen. Een string kan zijn interne array met chars delen met een andere string en bevat daarom een eigen beginpositie en lengte. Het char-array is een apart object, dat behalve de karakters ook een lengteparameter bevat.

Velden in een string
TypeLengte
objectheader 2x referentie 2x4 bytes
hash int 4 bytes
offset int 4 bytes
count int 4 bytes
value (char-array) referentie 4 bytes
Subtotaal 24 bytes
Velden in de char-array
objectheader 2x referentie 2x4 bytes
length int 4 bytes
char[0], char[1], ..., char[n] char n*2 bytes
padding van 2, 4 of 6 bytes
Subtotaal bij lege tekst 12 bytes
+ 4 bytes padding
Totaal voor lege tekst 40 bytes

Een lege string kost dus al 24+16, oftewel 40 bytes, overigens net als strings van 1 en 2 tekens. Uiteraard is de impact van de overhead kleiner bij langere strings.

Ook van andere veelgebruikte objecten kan het geen kwaad om te weten hoeveel data ze gebruiken. Een arraylist heeft bijvoorbeeld een object-array en een size-integer aan boord. Een lege arraylist kost dus in theorie 2*4+4+4=16 bytes plus 16 bytes voor de lege array, wat het totaal op 32 bytes brengt. Er zit echter een addertje onder het gras; een arraylist wordt standaard aangemaakt met een object-array van 10 elementen, die nog eens 2*4+4+10*4 bytes (+4 bytes padding) in beslag neemt; dat brengt het totaal op 72 bytes.

Voor hashmaps geldt een vergelijkbaar verhaal. Een hashmap bevat een array van entries, een size, een threshold, een modcount en een loadfactor. Daarnaast worden nog referenties naar de entryset, de keyset en de values bewaard. Dat kost dus 2*4+4*4+4+3*4=40 bytes. Daar komt nog een entry-array bij, die standaard 16 elementen groot is en dus 2*4+4+16*4=76 bytes omvat. Met padding betekent dat een grootte van 80 bytes. Een blanco hashmap beslaat dus 120 bytes en dan zit er nog geen data in.

Als je vooraf een grootte voor een hashmap opgeeft, wordt de capaciteit afgerond naar het eerstvolgende veelvoud van 2. De entry-objecten kosten weer 2*4 bytes voor de header en hebben daarnaast referenties naar de key, value en een eventuele next-entry. Daarnaast bevatten ze nog een kopie van de hashcode, wat een totaal van 24 bytes oplevert. Een met 5 elementen gevulde hashmap kost daardoor 40 + 12 + (4*8) + (24*5) = (+4 bytes padding) 208 bytes.

Elementen
Object[]
ArrayListLinkedListHashSet
10 56 bytes 72 bytes 264 bytes
356 bytes
100 416 bytes 432 bytes 2424 bytes 2980 bytes
1000 4016 bytes 4032 bytes 24024 bytes
28164 bytes

Met de voorgaande voorbeelden is waarschijnlijk wel duidelijk dat objecten in Java al gauw een stuk meer geheugen gebruiken dan je aan de hand van de opgeslagen data zou verwachten. Daarmee is de geheugenbehoefte van Java echter niet uitzonderlijk. Het objectmodel van PHP is bijvoorbeeld nog een stuk erger. Door de dynamische types van velden nemen 5 objecten met ieder 2 integers in een array maar liefst 4640 bytes in beslag.

In Java vergt dezelfde data de hierboven genoemde 208 bytes voor een hashmap, met daarnaast 5*16=80 bytes voor de integer-keys en 5*16=80 bytes voor de genoemde objecten. Dat brengt het totaal op 368 bytes en dat is al een stuk schappelijker.

Wat vind je van dit artikel?

Geef je mening in het Geachte Redactie-forum.

Nintendo Switch (OLED model) Apple iPhone 13 LG G1 Google Pixel 6 Call of Duty: Vanguard Samsung Galaxy S21 5G Apple iPad Pro (2021) 11" Wi-Fi, 8GB ram Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2022 Hosting door True