Garbage Collection in Java

Die Müllsammlung in Java ist ein fortgeschrittenes Thema. Das Wissen über die Java GC hilft uns, die Laufzeitleistung unserer Anwendung fein abzustimmen.

Müllsammlung in Java

  • In Java müssen sich die Programmierer nicht um die Zerstörung der nicht mehr verwendeten Objekte kümmern. Der Garbage Collector kümmert sich darum.
  • Der Garbage Collector ist ein Hintergrundprozess (Daemon-Thread), der kontinuierlich läuft. Im Wesentlichen befreit er den Heap-Speicher, indem er die unerreichbaren Objekte zerstört.
  • Unzugängliche Objekte sind solche, auf die von keinem Teil des Programms mehr verwiesen wird.
  • Wir können den Garbage Collector für unser Java-Programm über JVM-Optionen auswählen, auf die wir später in diesem Tutorial eingehen werden.

Wie funktioniert die automatische Müllsammlung?

Die automatische Müllsammlung ist ein Prozess, bei dem der Heap-Speicher betrachtet wird, unerreichbare Objekte identifiziert (auch als „Markierung“ bekannt) und sie mit Kompaktion zerstört. Ein Problem bei diesem Ansatz ist, dass mit zunehmender Anzahl von Objekten die Zeit für die Müllsammlung weiter zunimmt, da der gesamte Objektliste durchsucht werden muss, um das unerreichbare Objekt zu finden. Die empirische Analyse von Anwendungen zeigt jedoch, dass die meisten Objekte von kurzer Lebensdauer sind. Dieses Verhalten wurde verwendet, um die Leistung der JVM zu verbessern, und die angenommene Methodik wird allgemein als Generational Garbage Collection bezeichnet. Bei dieser Methode wird der Heap-Speicher in Generationen wie Young Generation, Old oder Tenured Generation und Permanent Generation unterteilt. Der Heap-Speicher der Young Generation ist der Bereich, in dem alle neuen Objekte erstellt werden. Sobald er gefüllt ist, findet eine geringfügige Müllsammlung (auch bekannt als Minor GC) statt. Das bedeutet, dass alle toten Objekte dieser Generation zerstört werden. Dieser Prozess ist schnell, weil, wie wir aus dem Diagramm sehen können, die meisten davon tot sein würden. Die überlebenden Objekte in der jungen Generation altern und wechseln schließlich zu den älteren Generationen. Die Old Generation wird verwendet, um lang überlebende Objekte zu speichern. Typischerweise wird ein Schwellenwert für Objekte der jungen Generation festgelegt, und wenn dieses Alter erreicht ist, wird das Objekt in die alte Generation verschoben. Schließlich muss die alte Generation eingesammelt werden. Dieses Ereignis wird als Major GC (major garbage collection) bezeichnet. Oft ist es viel langsamer, weil es alle lebenden Objekte umfasst. Es gibt auch Full GC, was bedeutet, dass der gesamte Heap – sowohl Young- als auch ältere Generationsspeicher – gereinigt 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, die alle generational sind. Jeder von ihnen hat seine eigenen Vor- und Nachteile. Die Wahl des zu verwendenden Garbage Collectors liegt bei uns, und es kann dramatische Unterschiede in der Durchsatzleistung und den Anwendungspausen geben. Alle diese teilen den verwalteten Heap in verschiedene Segmente auf und verwenden die alten Annahmen, dass die meisten Objekte im Heap kurzlebig sind und schnell recycelt werden sollten. Die vier Arten von Garbage Collectors sind also:

Serial GC

Dies ist der einfachste Garbage Collector, der für Single-Thread-Systeme und kleine Heap-Größen entwickelt wurde. Er friert alle Anwendungen während seiner Arbeit ein. Kann mit der JVM-Option -XX:+UseSerialGC aktiviert werden.

Parallel/Throughput GC

Dies ist der Standardcollector von JVM in JDK 8. Wie der Name schon sagt, verwendet er mehrere Threads, um den Heap-Speicher zu durchsuchen und Kompaktierung durchzuführen. Ein Nachteil dieses Collectors ist, dass er die Anwendungsthreads anhält, während er eine geringfügige oder vollständige GC durchführt. Er eignet sich am besten für Anwendungen, die solche Pausen verarbeiten können, und versuchen, die CPU-Überlastung durch den Collector zu optimieren.

Der CMS-Collector

Der CMS-Collector („concurrent-mark-sweep“)-Algorithmus verwendet mehrere Threads („concurrent“), um den Heap („mark“) nach unbenutzten Objekten zu durchsuchen, die recycelt werden können („sweep“). Dieser Collector geht in den Stop-The-World(STW)-Modus in zwei Fällen: – Während der Initialisierung der initialen Markierung von Wurzeln, d.h. Objekte in der alten Generation, die von Thread-Einstiegspunkten oder statischen Variablen erreichbar sind – Wenn die Anwendung den Zustand des Heaps geändert hat, während der Algorithmus gleichzeitig ausgeführt wurde, und ihn dazu zwingt, zurückzukehren und einige abschließende Anpassungen vorzunehmen, um sicherzustellen, dass die richtigen Objekte markiert sind. Dieser Collector kann mit Förderfehlern konfrontiert werden. Wenn einige Objekte aus der jungen Generation in die alte Generation verschoben werden sollen und der Collector nicht genügend Zeit hatte, Platz im Speicher der alten Generation zu schaffen, tritt ein Förderfehler auf. Um dies zu verhindern, können wir mehr Speicherplatz für die alte Generation bereitstellen oder dem Collector mehr Hintergrundthreads zur Verfügung stellen.

G1-Sammler

Zu guter Letzt ist der Garbage-First-Sammler, der für Speichergrößen größer als 4 GB entwickelt wurde. Er teilt die Speichergröße in Bereiche von 1 MB bis 32 MB auf, basierend auf der Speichergröße. Es gibt eine gleichzeitige globale Markierungsphase, um die Lebendigkeit von Objekten im gesamten Speicher zu bestimmen. Nach Abschluss der Markierungsphase weiß G1, welche Bereiche größtenteils leer sind. Es sammelt zunächst unerreichbare Objekte aus diesen Bereichen, was in der Regel eine große Menge an freiem Speicherplatz liefert. G1 sammelt also diese Bereiche (die Müll enthalten) zuerst und daher der Name Garbage-First. G1 verwendet auch ein Unterbrechungsvorhersagemodell, um ein benutzerdefiniertes Unterbrechungszeitziel zu erreichen. Es wählt die Anzahl der zu sammelnden Bereiche basierend auf dem angegebenen Unterbrechungszeitziel aus. Der G1-Garbage-Collection-Zyklus umfasst die Phasen, wie in der Abbildung gezeigt:

  1. Nur-Junge-Phase: Diese Phase umfasst nur die Objekte der jungen Generation und fördert sie in die alte Generation. Der Übergang zwischen der Nur-Junge-Phase und der Platz-Rückgewinnungsphase beginnt, wenn die alte Generation bis zu einem bestimmten Schwellenwert belegt ist, d.h. dem Initiating Heap Occupancy-Schwellenwert. Zu diesem Zeitpunkt plant G1 eine Initial Mark Nur-Junge-Sammlung anstelle einer regulären Nur-Junge-Sammlung.

  2. Erste Markierung: Diese Art der Sammlung startet den Markierungsprozess zusätzlich zu einer regulären Jung-alleinigen Sammlung. Die parallele Markierung bestimmt alle derzeit lebenden Objekte in den Regionen der alten Generation, die für die folgende Speicherwiedergewinnungsphase aufbewahrt werden sollen. Während die Markierung noch nicht vollständig abgeschlossen ist, können reguläre Jung-alleinige Sammlungen auftreten. Die Markierung endet mit zwei speziellen Stop-the-World-Pausen: Remark und Cleanup.

  3. Remark: Diese Pause finalisiert die Markierung selbst und führt eine globale Referenzverarbeitung und Klassenentladung durch. Zwischen Remark und Cleanup berechnet G1 eine Zusammenfassung der Lebendigkeitsinformationen nebenläufig, die in der Cleanup-Pause finalisiert und zur Aktualisierung interner Datenstrukturen verwendet wird.

  4. Bereinigung: Diese Pause betrifft auch die vollständig leeren Bereiche und bestimmt, ob tatsächlich eine Phasen zur Speicherfreigabe folgen wird. Wenn eine Phase zur Speicherfreigabe folgt, wird die nur-für-jung-Generation abgeschlossen mit einer einzigen nur-für-jung-Generation-Sammlung.

  5. Phasen zur Speicherfreigabe: Diese Phase besteht aus mehreren gemischten Sammlungen – zusätzlich zu den Bereichen der jungen Generation evakuiert sie auch lebende Objekte der Bereiche der alten Generation. Die Phase zur Speicherfreigabe endet, wenn G1 feststellt, dass das Evakuieren weiterer Bereiche der alten Generation nicht genügend freien Speicherplatz bringen würde, um den Aufwand zu rechtfertigen.

G1 kann mit der –XX:+UseG1GC-Flag aktiviert werden. Diese Strategie verringert die Wahrscheinlichkeit, dass der Heap erschöpft wird, bevor die Hintergrundthreads das Scannen nach unerreichbaren Objekten abgeschlossen haben. Außerdem komprimiert sie den Heap dynamisch, was der CMS-Collector nur im STW-Modus kann. In Java 8 wurde eine interessante Optimierung mit dem G1-Collector eingeführt, genannt String-Deduplizierung. Da die Zeichenarrays, die unsere Strings repräsentieren, einen Großteil des Heap-Speichers einnehmen, wurde eine neue Optimierung implementiert. Diese ermöglicht es dem G1-Collector, Strings zu identifizieren, die mehr als einmal im Heap dupliziert sind, und sie so zu ändern, dass sie auf dasselbe interne char[]-Array zeigen, um mehrfache Kopien desselben Strings im Heap zu vermeiden. Die Optimierung kann mit dem JVM-Argument -XX:+UseStringDeduplication aktiviert werden. G1 ist der Standard-Garbage-Collector in JDK 9.

Java 8 PermGen und Metaspace

Wie bereits erwähnt wurde der PermGen-Speicher seit Java 8 entfernt. Ab JDK 8 verwendet der HotSpot JVM den nativen Speicher für die Repräsentation von Klassenmetadaten, der Metaspace genannt wird. Die meisten Zuweisungen für die Klassenmetadaten erfolgen aus dem nativen Speicher. Es gibt auch eine neue Flagge namens MaxMetaspaceSize, um die Menge an Speicher für Klassenmetadaten zu begrenzen. Wenn wir keinen Wert dafür angeben, passt sich der Metaspace zur Laufzeit je nach Bedarf der Anwendung an. Die Garbage Collection des Metaspace wird ausgelöst, wenn die Nutzung der Klassenmetadaten das Limit von MaxMetaspaceSize erreicht. Eine übermäßige Garbage Collection des Metaspace kann ein Symptom für Speicherlecks von Klassen oder Classloadern oder eine unzureichende Dimensionierung unserer Anwendung sein. Das war es für die Garbage Collection in Java. Ich hoffe, Sie haben verstanden, welche verschiedenen Garbage Collector wir in Java haben. Referenzen: Oracle-Dokumentation, G1 GC.

Source:
https://www.digitalocean.com/community/tutorials/garbage-collection-in-java