In Java erfolgt die Speicherverwaltung durch die automatische Müllsammlung (Garbage Collector, GC). Die JVM überwacht eigenständig die Objekte im Speicher: Wenn auf ein Objekt keine Referenzen mehr vorhanden sind, wird es unerreichbar und kann gelöscht werden.
Der GC besteht aus verschiedenen Phasen, wie Markieren, Kehrung und manchmal Kompaktierung. In der Phase Markieren werden die Objekte, die von den Wurzeln (GC roots) erreicht werden können, als lebendig markiert. Danach beginnt die Kehrung: ungenutzte Objekte werden gelöscht. Manchmal wird auch Kompaktierung durchgeführt – eine Defragmentierung des Speichers.
Es gibt verschiedene Typen von GC: Serial, Parallel, CMS, G1. Diese sollten je nach Art der Last auf der Anwendung ausgewählt werden.
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // Objekte bleiben erreichbar }
In diesem Beispiel werden die Objekte niemals unerreichbar, der GC wird sie nicht löschen, und es tritt ein OutOfMemoryError auf.
Wenn ein Objekt keine Referenzen mehr hat, wann wird der Aufruf seines Finalizers (finalize()) garantiert?
Antwort: Der Aufruf der Methode finalize() ist überhaupt nicht garantiert! Sie kann keinmal oder mit einer Verzögerung aufgerufen werden. Man sollte sich nicht auf finalize() zur Freigabe von Ressourcen verlassen.
@Override protected void finalize() throws Throwable { // Könnte nicht ausgeführt werden! }
Geschichte
In einem großen e-commerce System hofften die Entwickler, die Bereinigung temporärer Dateien mit der Methode finalize() zu automatisieren, aber aufgrund des seltenen Aufrufs dieser Methode füllte der Cache sich mit temporären Dateien, was zu einem Mangel an Speicherplatz und Ausfallzeiten führte.
Geschichte
In einer High-Load REST API wurde versehentlich ein interner Cache von Referenzen auf große Objekte gespeichert, was die Freigabe von Speicher verhinderte. Nach dem Absturz der Anwendung mit OutOfMemoryError zeigte die Analyse einen Fehler in der Caching-Methode auf.
Geschichte
Finalizer wurden verwendet, um Netzwerkverbindungen freizugeben, da dies als "verlässliche Methode" angesehen wurde. Dies führte dazu, dass Verbindungen im System unbestimmt lange hingen blieben, was zu Blockaden und Erschöpfung des Verbindungspools führte.