Artikel

 
Juli 2009 | Artikel

Wie funktioniert der Java Garbage Collector

(Link zum Artikel: http://www.it-republik.de/jaxenter/artikel/2452)

Garbage Collection und Monitoring von Webanwendungen

Text: Kai Jäger
Eine Eigenschaft von Java ist, dass man sich nicht selbst um das Speichermanagement kümmern muss. Denn der Garbage Collector der Java Virtual Machine (JVM) erledigt dies zufriedenstellend. Jedoch kommt es vor, dass Java-Anwendungen im Produktivbetrieb mit einer OutOfMemoryError terminieren. Im Rahmen des Monitorings ist es nützlich, dies vorher zu erfassen, um Gegenmaßnahmen einleiten zu können. Dieser Artikel illustriert die Funktionsweise des Garbage Collectors und geht auf Metriken ein, die beim Monitoring unterstützen.
Teil 1   Teil 2   Teil 3   Teil 4   

Grundlegendes

Im Gegensatz zu C oder C++, die keine Garbage Collection (GC) verwenden, muss in Java dynamisch zugewiesener Speicher nicht explizit freigegeben werden. Nicht mehr referenzierte Objekte werden vom Garbage Collector automatisch erkannt und entfernt. Somit werden einige prominente Fehlerquellen eliminiert, die noch referenziert werden, z. B. das vorzeitige Freigeben von Objekten oder das Vergessen nicht mehr referenzierter Objekte. Jedoch erzeugt das automatisierte Einsammeln durch die Laufzeitumgebung einen gewissen Overhead.

Die Organisation des Heap-Speichers in Java, hier am Beispiel der Sun-Implementierung, lässt sich vereinfacht wie folgt wiedergeben (Abb. 1): Bei der Allokation eines Objekts wird es zunächst in der Young Generation (Nursery) angelegt. "Überlebt" es einige Zeit, wird es noch referenziert, also in die Old Generation (Tenured) "befördert". Da ein Großteil der erzeugten Objekte nicht referenziert ist, kann der Garbage Collector in der Young Generation mit einem schnellen Algorithmus alle nicht referenzierten Objekte entfernen. In der Old Generation, die normalerweise den Hauptbestandteil des Heaps ausmacht, wird ein langsamerer, aber speicherplatzeffizienterer Algorithmus verwendet.

Die Permanent Generation (PermGen) enthält Objekte, die gemäß Vorgabe der Java Virtual Machine vom Garbage Collector verwaltet werden sollen. Das sind z. B. Objekte, die Klassen und Methoden beschreiben, sowie die Klassen und Methoden selbst. Der PermGen gehört nicht zum Heap.

Es existieren also zwei unterschiedliche Varianten der Garbage Collection: Die schnelle GC in der Young Generation, die sehr häufig ausgeführt wird (Minor Collection) und die langsamere GC in der Old Generation (Full Collection). Wichtig ist hierbei, dass sowohl bei den Minor Collections als auch bei den Full Collections eine "stop the world"-Pause eingelegt wird, also alles andere außer der Garbage Collection angehalten und nach Beendigung der Operation fortgesetzt wird. Dies dauert je nach Collector-Art unterschiedlich lange.

Garbage-Collection-Algorithmen

Die folgenden Abschnitte geben einen Überblick über die unterschiedlichen GC-Algorithmen der Young, Old und Permanent Generation. Dabei wird nicht auf alle GC-Algorithmen eingegangen. Der Speicherbereich der Young Generation ist unterteilt in Eden und zwei gleich große Survivor Spaces, oft From-Survivor und To-Survivor genannt. Der Eden Space macht hierbei den Großteil des Speichers aus. Neue Objekte werden im Eden Space generiert. Sind sie zu groß für diesen, werden sie direkt in der Old Generation erzeugt (Abb. 2).

Beginnt der Garbage Collector seine Arbeit, werden alle noch referenzierten Objekte im Eden Space und From-Survivor in den To-Survivor kopiert. Objekte, die sich schon einige Zeit in den Survivor Spaces befanden, werden in die Old Generation verschoben. Anschließend werden der Eden Space und der From-Survivor geleert und die beiden Survivor Spaces "tauschen" die Funktion: Aus dem From-Survivor wird der To-Survivor und umgekehrt. Denn der Großteil aller Objekte im Eden Space wird nicht oder nur kurz referenziert, z. B. Iterator-Objekte oder sonstige Schleifenvariablen. Durch das Kopieren in die Survivor Spaces wird sichergestellt, dass solche Objekte nicht direkt in die Old Generation verschoben werden, nur weil sie zum Zeitpunkt der Garbage Collection noch eine Referenz trugen.

In einem einfachen Beispiel lässt sich der Vorgang wie folgt darstellen (Abb. 3): Das erste Bild zeigt symbolisch die Speicherbelegung der Young Generation. Die Objekte werden durch ihrer Größe entsprechende Blöcke angezeigt. Die grünen Objekte werden momentan referenziert, die roten sind nicht mehr referenziert. Wird nun ein neues Objekt angelegt, ist kein Platz mehr im Eden Space vorhanden. Der Garbage Collector nimmt seine Arbeit auf. Er verschiebt alle Objekte, die noch referenziert sind (hier 1, 3, 5 und 9), in den To-Survivor Space. In diesem Beispiel wird das Objekt 5 – aufgrund der Zeit, die es in den Survivor Spaces "überlebt" hat – in die Old Generation verschoben. Sind mehr Objekte referenziert, als in den To-Survivor Space passen, werden die restlichen direkt in die Old Generation verschoben. Abschließend werden der Eden Space und der From-Survivor Space geleert, also die Objekte 2, 4, 6, 7 und 8 endgültig gelöscht. Nach Abschluss einer Garbage Collection in der Young Generation ist daher der gesamte Speicher bis auf einen der beiden Survivor Spaces leer.

Teil 1   Teil 2   Teil 3   Teil 4   

Anzeige

Kommentare

Gravatar Ingo 22.07.2009
um 12:40 Uhr
Hallo,

Wenn man einen Artikel ueber Speicherverwaltung in Java schreibt, sollte man wissen, dass es ein OutOfMemoryError ist und keine Exception.

Weiterhin macht der CMS im Normalfall keinen Stop-The-World um den Tenured Space aufzuraeumen bzw. nur sehr kurz, mark und sweep finden concurrent statt, siehe z.B. http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html#0.0.0.0.%2520Concurrent%2520Phases%257Coutline.

Ueberhaupt vermisse ich in dem Artikel jeglichen Hinweis darueber, dass es mittlerweile sehr unterschiedliche Garbage-Collector-Strategien in der JavaVM gibt (z.B. CMS, ParallelGC, GarbageFirst), die je nach Anwendungssituation benutzt werden sollten (z.B. CMS fuer Throughput, ParallelGC fuer Rechenleistung).

Gruesse,

Ingo
#zitieren
Gravatar Kai Jäger 23.07.2009
um 12:13 Uhr
Hallo Ingo,

vielen Dank für den Hinweis auf die falsche Bezeichnung des OutOfMemoryError. Hier hat sich leider der Fehlerteufel in meinem Artikel eingeschlichen.

Dieser Artikel behandelt schwerpunktmäßig das Thema „Monitoring von Webanwendungen“. Da im Java Magazin 08/2009 bereits sehr detailliert auf die Funktionsweise des Garbage Collector eingegangen wurde, liegt der Fokus hier vor allem auf dem Zusammenspiel vom Garbage Collector und Monitoring. Eine detaillierte Erläuterung sämtlicher Garbage Collection Algorithmen wäre meines Erachtens zu weitführend gewesen.

Viele Grüße,
Kai
#zitieren
Gravatar Rainer 05.08.2009
um 13:22 Uhr
Hallo,

wie sieht der zugehörige java-Befehl aus, mit dem das Testprogramm gestartet wurde ? Die Commandline-Optionen wären interessant.

Gruss,
Rainer
#zitieren
Gravatar Kai Jäger 05.08.2009
um 14:16 Uhr
Hallo Rainer,

der Aufruf zur Analyse erfolgte mittels des Profilers von Netbeans 6.5 mit dem Parameter -Xmx64m.

Alternativ könnte man auch direkt VisualVM verwenden und die Applikation mit java de.nobiscum.kja.garbagecollectortest.Main -Xmx=64m starten.

Viele Grüße,
Kai
#zitieren

Anzeige

zurück zum Seitenanfang