Garbage Collection in Java

Die Garbage Collection in Java ist eines der fortgeschrittenen Themen. Kenntnisse über Java-GC helfen uns, die Laufzeitleistung unserer Anwendungen zu optimieren.

Garbage Collection in Java

In Java müssen sich Programmierer nicht darum kümmern, nicht mehr benötigte Objekte zu zerstören. Der Garbage Collector übernimmt diese Aufgabe. Der Garbage Collector in Java ist ein Daemon-Thread, der im Hintergrund ausgeführt wird. Grundsätzlich gibt er den Heap-Speicher frei, indem er nicht erreichbare Objekte zerstört. Nicht erreichbare Objekte sind solche, die von keinem Teil des Programms mehr referenziert werden. Wir können den Garbage Collector für unser Java-Programm über JVM-Optionen auswählen, auf die wir in einem späteren Abschnitt dieses Tutorials eingehen werden.

Wie funktioniert die automatische Garbage Collection in Java?

Die automatische Garbage Collection in Java ist ein Prozess, bei dem der Heap-Speicher untersucht, die nicht erreichbaren Objekte (auch als „Markierung“ bekannt) identifiziert und durch Komprimierung zerstört werden. Ein Problem bei diesem Ansatz ist, dass die Garbage Collection-Zeit mit zunehmender Anzahl von Objekten steigt, da die gesamte Objektliste durchsucht werden muss, um das nicht erreichbare Objekt zu finden. Empirische Analysen von Anwendungen zeigen jedoch, dass die meisten Objekte nur kurzlebig sind. Dieses Verhalten wurde genutzt, um die Leistung der JVM zu verbessern, und die angewandte Methodik wird allgemein als „Generational Garbage Collection“ bezeichnet. Bei diesem Verfahren wird der Heap-Speicher in Generationen wie „Young Generation“, „Old oder Tenured Generation“ und „Permanent Generation“ unterteilt. Der Heap-Bereich der „Young Generation“ ist der Bereich, in dem alle neuen Objekte erstellt werden. Sobald er gefüllt ist, findet eine „Minor Garbage Collection“ (auch als Minor GC bekannt) statt, was bedeutet, dass alle toten Objekte aus dieser Generation zerstört werden. Dieser Prozess ist schnell, da, wie man aus dem Diagramm sehen kann, die meisten von ihnen tot wären. Die überlebenden Objekte in der jungen Generation altern und werden schließlich in die älteren Generationen verschoben. Die „Old Generation“ dient zur Speicherung langlebiger Objekte. Typischerweise wird für Objekte der „Young Generation“ eine Schwelle festgelegt, und wenn dieses Alter erreicht ist, wird das Objekt in die „Old Generation“ verschoben. Schließlich muss auch die „Old Generation“ gesammelt werden. Dieses Ereignis wird als „Major GC“ (große Garbage Collection) bezeichnet. Sie ist oft wesentlich langsamer, da sie alle lebenden Objekte betrifft. Es gibt auch „Full GC“, was bedeutet, dass der gesamte Heap – sowohl die „Young Generation“ als auch die „Old Generation“ – bereinigt wird. Schließlich gab es bis Java 7 eine „Permanent Generation“ (oder Perm Gen), die Metadaten enthielt, die von der JVM benötigt wurden, um die in der Anwendung verwendeten Klassen und Methoden zu beschreiben. Sie wurde in Java 8 entfernt.

Java Garbage Collectors

Die JVM bietet tatsächlich vier verschiedene Garbage Collector für die Garbage Collection in Java an, die alle generativ sind. Jeder hat seine eigenen Vor- und Nachteile. Die Wahl des zu verwendenden Garbage Collectors liegt bei uns, und es kann erhebliche Unterschiede in der Durchsatzrate und den Anwendungspausen geben. Alle teilen den verwalteten Heap in verschiedene Segmente auf, basierend auf der Annahme, dass die meisten Objekte im Heap kurzlebig sind und schnell recycelt werden sollten. Die vier Arten von Garbage Collectors in Java sind:

Serial GC

Dies ist der einfachste Garbage Collector, der für Einzelprozessorsysteme und kleine Heap-Größen ausgelegt ist. Er friert alle Anwendungen während des Betriebs ein. Kann mit der JVM-Option -XX:+UseSerialGC aktiviert werden.

Parallel/Throughput GC

Dies ist der Standard-Collector der JVM in JDK 8. Wie der Name schon sagt, verwendet er mehrere Threads, um den Heap-Speicher zu durchsuchen und zu komprimieren. Ein Nachteil dieses Collectors besteht darin, dass er die Anwendungsthreads beim Ausführen von „Minor“ oder „Full GC“ pausiert. Er eignet sich am besten für Anwendungen, die solche Pausen tolerieren können und versucht, die durch den Collector verursachte CPU-Belastung zu optimieren.

Der CMS-Collector

Der CMS-Collector (Concurrent-Mark-Sweep) Algorithmus verwendet mehrere Threads („concurrent“), um den Heap („mark“) nach ungenutzten Objekten zu durchsuchen, die recycelt werden können („sweep“). Dieser Collector wechselt in den „Stop-The-World“ (STW)-Modus in zwei Fällen:

  • Bei der Initialisierung der anfänglichen Markierung von Wurzeln, d. h. Objekten in der „Old Generation“, die von Thread-Einstiegspunkten oder statischen Variablen erreichbar sind
  • Wenn die Anwendung den Zustand des Heaps geändert hat, während der Algorithmus parallel lief, und sie gezwungen ist, einige abschließende Änderungen vorzunehmen, um sicherzustellen, dass die richtigen Objekte markiert sind.

Dieser Collector kann auf Förderungsfehler stoßen. Wenn einige Objekte aus der „Young Generation“ in die „Old Generation“ verschoben werden sollen und der Collector nicht genügend Zeit hatte, Platz in der „Old Generation“ zu schaffen, tritt ein Förderungsfehler auf. Um dies zu verhindern, können wir dem „Old Generation“-Bereich mehr Heap-Speicher zur Verfügung stellen oder mehr Hintergrundthreads für den Collector bereitstellen.

G1-Collector

Zuletzt, aber nicht weniger wichtig, ist der Garbage-First Collector, der für Heap-Größen über 4 GB ausgelegt ist. Er teilt den Heap in Regionen auf, die je nach Heap-Größe von 1 MB bis 32 MB reichen. Es gibt eine parallele globale Markierungsphase, um die Lebendigkeit von Objekten im gesamten Heap zu bestimmen. Nach Abschluss der Markierungsphase weiß der G1, welche Regionen größtenteils leer sind. Er sammelt zuerst die nicht erreichbaren Objekte aus diesen Regionen, was in der Regel eine große Menge an freiem Speicherplatz ergibt. Daher sammelt der G1 zuerst diese Regionen (die Müll enthalten), daher der Name Garbage-First. Der G1 verwendet auch ein Pause-Vorhersagemodell, um eine benutzerdefinierte Pausenzeit einzuhalten. Er wählt die Anzahl der zu sammelnden Regionen basierend auf der angegebenen Pausenzeit aus. Der G1-Garbage-Collection-Zyklus umfasst die Phasen wie im Bild dargestellt:

Young-only Phase

Diese Phase umfasst nur die Objekte der „Young Generation“ und befördert sie in die „Old Generation“. Der Übergang zwischen der „Young-only“-Phase und der „Space-Reclamation“-Phase beginnt, wenn die „Old Generation“ bis zu einem bestimmten Schwellenwert belegt ist, d. h. dem „Initiating Heap Occupancy“-Schwellenwert. Zu diesem Zeitpunkt plant der G1 eine Initial-Markierungssammlung der jungen Generation anstelle einer regulären Sammlung der jungen Generation.

Initiale Markierung

Diese Art der Sammlung startet den Markierungsprozess zusätzlich zu einer regulären „Young-only“-Sammlung. Die parallele Markierung bestimmt alle aktuell lebenden Objekte in den Regionen der „Old Generation“, die für die folgende „Space-Reclamation“-Phase beibehalten werden sollen. Während die Markierung noch nicht vollständig abgeschlossen ist, können reguläre „Young-only“-Sammlungen auftreten. Die Markierung endet mit zwei speziellen Stop-The-World-Pausen: „Remark“ und „Cleanup“.

Remark

Diese Pause finalisiert die Markierung selbst und führt die globale Referenzverarbeitung und das Klassenentladen durch. Zwischen „Remark“ und „Cleanup“ berechnet G1 eine Zusammenfassung der Lebendigkeit der Informationen parallel, die in der „Cleanup“-Pause abgeschlossen und zur Aktualisierung der internen Datenstrukturen verwendet wird.

Cleanup

Diese Pause umfasst auch die vollständig leeren Regionen und bestimmt, ob tatsächlich eine „Space-Reclamation“-Phase folgen wird. Falls eine „Space-Reclamation“-Phase folgt, endet die „Young-only“-Phase mit einer einzigen „Young-only“-Sammlung.

Space-Reclamation-Phase

Diese Phase besteht aus mehreren gemischten Sammlungen – zusätzlich zu den Regionen der „Young Generation“ werden auch lebende Objekte der „Old Generation“ evakuiert. Die „Space-Reclamation“-Phase endet, wenn G1 feststellt, dass das Evakuieren weiterer „Old Generation“-Regionen nicht genügend freien Speicherplatz ergeben würde, um den Aufwand zu rechtfertigen.

Der G1 kann mit dem –XX:+UseG1GC-Flag aktiviert werden. Diese Strategie reduzierte die Wahrscheinlichkeit, dass der Heap erschöpft ist, bevor die Hintergrund-Threads das Scannen nach nicht erreichbaren Objekten abgeschlossen haben. Außerdem komprimiert er den Heap „on-the-go“, was der CMS-Collector nur im STW-Modus tun kann. In Java 8 wurde

eine Optimierung mit dem G1-Collector eingeführt, die als String-Deduplikation bezeichnet wird. Wie wir wissen, belegen die Zeichenarrays, die unsere Strings darstellen, viel Speicherplatz im Heap. Eine neue Optimierung ermöglicht es dem G1-Collector, Strings zu erkennen, die mehrmals im Heap dupliziert wurden, und diese so zu modifizieren, dass sie auf dasselbe interne char[]-Array verweisen, um Mehrfachkopien desselben Strings im Heap zu vermeiden. Wir können das JVM-Argument -XX:+UseStringDeduplication verwenden, um diese Optimierung zu aktivieren. G1 ist der Standard-Garbage-Collector in JDK 9.

Java 8 PermGen und Metaspace

Wie bereits erwähnt, wurde der Permanent Generation-Bereich seit Java 8 entfernt. Jetzt verwendet die HotSpot-JVM von JDK 8 den nativen Speicher für die Darstellung von Klassenmetadaten, der als Metaspace bezeichnet wird. Die meisten Zuweisungen für die Klassenmetadaten erfolgen außerhalb des nativen Speichers. Es gibt auch eine neue Flagge namens MaxMetaspaceSize, um die Menge an Speicher zu begrenzen, die für Klassenmetadaten verwendet wird. Wenn wir keinen Wert dafür angeben, wird die Metaspace-Größe zur Laufzeit je nach Bedarf der ausgeführten Anwendung neu dimensioniert. Die Garbage Collection des Metaspace wird ausgelöst, wenn die Nutzung der Klassenmetadaten das MaxMetaspaceSize-Limit erreicht. Übermäßige Metaspace-Garbage-Collection kann ein Hinweis auf Speicherlecks bei Klassen oder Classloadern oder auf eine unzureichende Dimensionierung für unsere Anwendung sein. Das war es zur Garbage Collection in Java. Ich hoffe, Sie haben ein Verständnis für die verschiedenen Garbage Collector, die wir in Java haben, gewonnen.

Referenzen: Oracle Documentation, G1 GC.

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren:

centron Managed Cloud Hosting in Deutschland

Das Lösen des Rucksackproblems

Linux Basics, Tutorial
Das Rucksackproblem: Eine Lösung mit C++ In diesem Artikel lernen wir, das Rucksackproblem mit C++ zu lösen. Wir beginnen mit der Problemstellung und gehen dann zur Lösung über. Dieses Problem…
centron Managed Cloud Hosting in Deutschland

Befehle für Prozessverwaltung in Linux

Anleitung, Linux Basics
Befehle für Prozessverwaltung in Linux Die Prozessverwaltung ist ein kritischer Aspekt der Systemadministration in Linux. Ein Prozess in Linux stellt eine laufende Instanz eines Programms dar. Jeder ausgeführte Befehl startet…