In Java wordt geheugenbeheer uitgevoerd via automatische garbage collection (Garbage Collector, GC). De JVM houdt automatisch de objecten in het geheugen in de gaten: als er geen referenties meer naar een object zijn, wordt het onbereikbaar en kan het worden verwijderd.
GC bestaat uit verschillende fasen, zoals Mark, Sweep en soms Compact. In de Mark-fase worden objecten die bereikbaar zijn vanuit de wortels (GC roots) gemarkeerd als levend. Vervolgens begint de Sweep-fase: ongebruikte objecten worden verwijderd. Soms wordt Compact uitgevoerd — geheugendefragmentatie.
Er zijn verschillende soorten GC: Serial, Parallel, CMS, G1. Deze moeten worden gekozen afhankelijk van het type belasting op de applicatie.
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // Objecten blijven bereikbaar }
In dit voorbeeld zullen de objecten nooit onbereikbaar worden, de GC zal ze niet verwijderen en er zal een OutOfMemoryError optreden.
Als er geen referenties meer naar een object zijn, wanneer is de aanroep van zijn finalizer (finalize()) gegarandeerd?
Antwoord: De aanroep van de methode finalize() is helemaal niet gegarandeerd! Het kan mogelijk nooit of met vertraging worden aangeroepen. Men kan niet rekenen op finalize() voor het vrijgeven van bronnen.
@Override protected void finalize() throws Throwable { // Kan mogelijk niet uitgevoerd worden! }
Verhaal
In een groot e-commerce systeem hoopten ontwikkelaars de opruiming van tijdelijke bestanden te automatiseren met behulp van de finalize()-methode, maar door de zeldzame aanroep van deze methode liep de cache vol met tijdelijke bestanden, wat leidde tot schijfruimtegebrek en stilstand.
Verhaal
In een high-load REST API werd per ongeluk een interne cache van referenties naar grote objecten opgeslagen, wat het vrijgeven van geheugen verhinderde. Na de crash van de applicatie met een OutOfMemoryError wees de analyse op een fout in de cachemethode.
Verhaal
Finalizers werden gebruikt voor het vrijgeven van netwerkverbindingen, in de veronderstelling dat dit een "betrouwbare manier" was. Dit leidde ertoe dat verbindingen onbepaald lang in het systeem bleven hangen, wat blokkades en uitputting van de verbindingpool veroorzaakte.