Sieben Typen von JAVA Garbage Collectors

In diesem Dokument wird erklärt, was der Garbage Collector in Java ist und die Haupttypen von Garbage Collectors mit dem Verhalten der einzelnen Garbage Collectors. Bitte beachten Sie auch, dass ich in diesem Artikel nicht erkläre, wie die Heap-Allokation erfolgt und Major/Minor Garbage Collectors starten. Das wird im nächsten Artikel erklärt.

Java ist eine objektorientierte Programmiersprache, die eine automatische Garbage Collection beinhaltet. Java weist automatisch Speicher zu und gibt ihn wieder frei, so dass Programme nicht mit dieser Aufgabe belastet werden. Jetzt (Java-12) hat Java sieben Arten von Garbage Collectors,

  1. Serieller Garbage Collector
  2. Paralleler Garbage Collector
  3. CMS Garbage Collector
  4. G1 Garbage Collector
  5. Epsilon Garbage Collector
  6. Z Garbage Collector
  7. Shenandoah Garbage Collector

5. und 6. Garbage Collector wurden in Java 11 eingeführt und der 7. wurde in Java 12 eingeführt. In den meisten Anwendungen in der Produktion werden die ersten vier Arten von Garbage Collectors verwendet. Da die letzten drei Garbage Collectors vor kurzem in das Bild kommen.

Müssen wir uns um GCs kümmern?

Ja, in der Tat. Wir müssen uns über den GC und sein Verhalten Gedanken machen. Denn sie kann einen erheblichen Leistungsunterschied bewirken. Jede GC hat ihre eigenen Vor- und Nachteile. Als Entwickler müssen wir eine klare Vorstellung vom Verhalten aller Garbage-Collectors haben und einen Garbage-Collector auswählen, der auf unser Geschäftsszenario abgestimmt ist. Wir können einen Garbage Collector auswählen, indem wir die Auswahl als JVM-Argument übergeben.

Serial Garbage Collector

Dies ist die einfachste GC-Implementierung. Sie ist grundsätzlich für eine Single-Thread-Umgebung konzipiert. Diese GC-Implementierung friert alle Anwendungsthreads ein, wenn sie ausgeführt wird. Sie verwendet einen einzigen Thread für die Garbage Collection. Daher ist es keine gute Idee, sie in Multi-Thread-Anwendungen wie Serverumgebungen zu verwenden.

Um den seriellen Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseSerialGC -jar Application.java

Paralleler Garbage Collector

Der parallele Garbage Collector wird auch als Durchsatzkollektor bezeichnet. Im Gegensatz zum seriellen Garbage Collector verwendet dieser mehrere Threads für die Garbage Collection. Ähnlich wie der serielle Garbage Collector friert auch dieser alle Anwendungsthreads ein, während er die Garbage Collection durchführt. Der Garbage Collector eignet sich am besten für Anwendungen, die Anwendungspausen ertragen können.

Um den parallelen Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseParallelGC -jar Application.java

Wenn wir diesen GC verwenden, können wir die maximalen Garbage-Collector-Threads und die Pausenzeit, den Durchsatz und den Footprint (Heap-Größe) angeben:

Die Anzahl der Garbage-Collector-Threads kann mit der Kommandozeilenoption

-XX:ParallelGCThreads=<N>

Das maximale Pausenzeitziel (Abstand zwischen zwei GC) wird mit der Kommandozeilenoption

-XX:MaxGCPauseMillis=<N>

Das maximale Durchsatzziel (gemessen an der für die Garbage Collection aufgewendeten Zeit gegenüber der außerhalb der Garbage Collection verbrachten Zeit) wird mit der Kommandozeilenoption

-XX:GCTimeRatio=<N>

Ein maximaler Heap-Footprint (die Menge an Heap-Speicher, die ein Programm während der Ausführung benötigt) wird mit der Option -Xmx<N> angegeben.

CMS Garbage Collector

Concurrent Mark Sweep (CMS) Garbage Collector verwendet mehrere Garbage Collector Threads für die Garbage Collection. Er durchsucht den Heap-Speicher, um Instanzen für die Räumung zu markieren und dann die markierten Instanzen zu durchsuchen. Er wurde für Anwendungen entwickelt, die kürzere Garbage-Collection-Pausen bevorzugen und es sich leisten können, Prozessorressourcen mit dem Garbage-Collector zu teilen, während die Anwendung läuft.

Der CMS-Garbage-Collector hält alle Anwendungsthreads nur in den folgenden zwei Szenarien

  1. Während der Markierung der referenzierten Objekte im alten Generationsraum.
  2. Jede Änderung im Heap-Speicher parallel zur Durchführung der Garbage Collection

Im Vergleich zum parallelen Garbage Collector verwendet der CMS-Collector mehr CPU, um einen besseren Anwendungsdurchsatz zu gewährleisten. Wenn wir mehr CPU für eine bessere Leistung zuweisen können, dann ist der CMS-Garbage-Collector die bevorzugte Wahl gegenüber dem parallelen Collector.

Um den CMS-Garbage-Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+USeParNewGC -jar Application.java

G1 Garbage Collector

G1 (Garbage First) Garbage Collector ist für Anwendungen gedacht, die auf Multiprozessor-Maschinen mit großem Speicherplatz laufen. Er ist seit JDK7 Update 4 und in späteren Versionen verfügbar.

Er teilt den Heap-Speicher in Regionen auf und sammelt diese parallel. G1 kompaktiert den freien Heap-Speicher auch gleich nach der Rückgewinnung des Speichers. Der CMS-Garbage-Collector hingegen verdichtet den Speicher in Stop-the-World-Situationen (STW). Der G1-Kollektor wird den CMS-Kollektor ersetzen, da er leistungsfähiger ist.

Der G1-Kollektor besteht aus zwei Phasen:

  1. Marking
  2. Sweeping

Im Gegensatz zu anderen Kollektoren unterteilt der G1-Kollektor den Heap in eine Reihe gleich großer Heap-Regionen, die jeweils einen zusammenhängenden Bereich des virtuellen Speichers darstellen. Bei der Durchführung von Garbage Collections zeigt G1 eine gleichzeitige globale Markierungsphase, um die Lebendigkeit von Objekten im gesamten Heap zu bestimmen.

Nach Abschluss der Markierungsphase weiß G1, welche Regionen überwiegend leer sind. Es sammelt zuerst in diesen Bereichen, was in der Regel eine erhebliche Menge an freiem Platz ergibt. Deshalb wird diese Methode der Garbage Collection auch Garbage-First genannt.

Um den G1 Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseG1GC -jar Application.java

Epsilon Garbage Collector

Epsilon ist ein nicht-operativer oder passiver Garbage Collector. Er weist den Speicher für die Anwendung zu, sammelt aber nicht die ungenutzten Objekte ein. Wenn die Anwendung den Java-Heap erschöpft, wird die JVM heruntergefahren. Das bedeutet, dass der Epsilon Garbage Collector es zulässt, dass Anwendungen keinen Speicher mehr haben und abstürzen.

Der Zweck dieses Garbage Collectors ist die Messung und Verwaltung der Anwendungsleistung. Aktive Garbage Collectors sind komplexe Programme, die innerhalb der JVM neben der Anwendung laufen. Epsilon beseitigt die Auswirkungen, die GC auf die Leistung hat. Es gibt keine GC-Zyklen oder Lese- und Schreibsperren. Bei Verwendung der Epsilon GC läuft der Code isoliert. Epsilon hilft bei der Visualisierung, wie sich die Garbage Collection auf die Leistung der Anwendung auswirkt und wie hoch der Schwellenwert für den Speicherbedarf ist, da es anzeigt, wann dieser zu Ende geht. Wenn wir zum Beispiel denken, dass wir nur ein Gigabyte Speicher für unsere Anwendung benötigen, können wir sie mit -Xmx1g ausführen und das Verhalten sehen. Wenn diese Speicherzuteilung nicht ausreicht, führen Sie es mit einem Heap-Dump erneut aus. Bitte beachten Sie, dass wir diese Option aktivieren müssen, um einen Heap-Dump zu erhalten. Wir können dieses Argument verwenden, um einen Heap-Dump zu erhalten, wenn die Anwendung aufgrund von Speichermangel abstürzt.

XX:HeapDumpOnOutOfMemoryError

Wenn wir jedes Quäntchen Leistung aus unserer Anwendung herausholen müssen, könnte Epsilon die beste Option für einen GC sein. Aber wir müssen genau wissen, wie unser Code den Speicher nutzt. Wenn er so gut wie keinen Müll erzeugt oder Sie genau wissen, wie viel Speicher er in dem Zeitraum, in dem er läuft, verbraucht, ist Epsilon eine brauchbare Option.

Um den Epsilon Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseEpsilonGC -jar Application.java

Z-Garbage-Collector

ZGC führt alle teuren Arbeiten gleichzeitig aus, ohne die Ausführung von Anwendungs-Threads für mehr als 10ms anzuhalten, was ihn für Anwendungen geeignet macht, die eine geringe Latenzzeit benötigen und/oder einen sehr großen Heap verwenden. Laut der Oracle-Dokumentation kann es mit Heaps von mehreren Terabyte umgehen. Oracle hat ZGC in Java 11 eingeführt. Der Z-Garbage-Collector führt seine Zyklen in seinen Threads durch. Er unterbricht die Anwendung für durchschnittlich 1 ms. Die Kollektoren G1 und Parallel benötigen im Durchschnitt etwa 200 ms.

In Java 12 fügte Oracle Leistungsverbesserungen und das Entladen von Klassen hinzu, obwohl sich Z noch im experimentellen Status befindet. Es ist nur auf 64-Bit-Linux verfügbar. ZGC nutzt jedoch die Vorteile von 64-Bit-Zeigern mit einer Technik namens Pointer Coloring. Farbige Zeiger speichern zusätzliche Informationen über Objekte auf dem Heap. Dies ist einer der Gründe, warum es auf die 64-Bit-JVM beschränkt ist. In diesem Artikel wurde dieses Szenario ausführlich erläutert (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).

ZGC führt seine Markierung in drei Phasen durch.

1. Kurze Stop-the-world-Phase – Sie untersucht die GC-Roots, lokale Variablen, die auf den Rest des Heaps zeigen. Die Gesamtzahl dieser Wurzeln ist in der Regel minimal und skaliert nicht mit der Größe der Last, so dass die Pausen von ZGC sehr kurz sind und nicht zunehmen, wenn der Heap wächst.

2. Gleichzeitige Phase – Sie geht den Objektgraphen durch und untersucht die farbigen Zeiger, wobei sie zugängliche Objekte markiert. Die Lastbarriere verhindert Konflikte zwischen der GC-Phase und den Aktivitäten der Anwendung.

3. Relokationsphase – Sie verschiebt lebende Objekte, um große Bereiche des Heaps freizugeben und so die Zuweisungen zu beschleunigen. Wenn die Verlagerungsphase beginnt, teilt ZGC den Heap in Seiten auf und arbeitet an einer Seite nach der anderen. Sobald ZGC das Verschieben von Roots beendet hat, erfolgt der Rest der Verlagerung in einer gleichzeitigen Phase.

ZGC versucht, die Anzahl der Threads selbst festzulegen, und liegt damit normalerweise richtig. Aber wenn ZGC zu viele Threads hat, wird es Ihre Anwendung aushungern. Wenn es nicht genug hat, erzeugen Sie schneller Müll, als der GC ihn einsammeln kann. Die Phasen von ZGC zeigen, wie er große Heaps verwaltet, ohne die Leistung zu beeinträchtigen, wenn der Anwendungsspeicher wächst.

Um Z Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseZGC -jar Application.java

Shenandoah

Shenandoah ist ein Garbage-Collector mit extrem geringer Pausenzeit, der die GC-Pausenzeiten reduziert, indem er mehr Garbage-Collection-Arbeiten gleichzeitig mit dem laufenden Java-Programm durchführt. CMS und G1 führen beide eine gleichzeitige Markierung von aktiven Objekten durch. Shenandoah fügt eine gleichzeitige Verdichtung hinzu.

Shenandoah verwendet Speicherregionen, um zu verwalten, welche Objekte nicht mehr in Gebrauch sind und welche live und bereit für die Verdichtung sind. Shenandoah fügt außerdem jedem Heap-Objekt einen Weiterleitungszeiger hinzu und verwendet ihn, um den Zugriff auf das Objekt zu kontrollieren. Das Design von Shenandoah tauscht gleichzeitige CPU-Zyklen und Speicherplatz gegen Verbesserungen der Pausenzeit. Der Forwarding Pointer macht es einfach, Objekte zu verschieben, aber die aggressiven Verschiebungen bedeuten, dass Shenandoah mehr Speicher verbraucht und mehr parallele Arbeit als andere GCs benötigt. Aber es erledigt die zusätzliche Arbeit mit sehr kurzen Stop-the-World-Pausen.

Shenandoah verarbeitet den Heap in vielen kleinen Phasen, von denen die meisten gleichzeitig mit der Anwendung ablaufen. Dieses Design ermöglicht es dem GC, einen großen Heap effizient zu verwalten.

  1. Erste Stop-the-world-Pause im Zyklus. Sie bereitet den Heap für die gleichzeitige Markierung vor und scannt die Wurzelmenge. Wie beiZGC entspricht die Länge dieser Pause der Größe des Root-Sets, nicht des Heaps.
  2. Nachfolgend durchläuft eine konkurrierende Phase den Heap und identifiziert erreichbare und unerreichbare Objekte.
  3. Die dritte Phase beendet den Markierungsprozess, indem sie ausstehende Heap-Aktualisierungen ablässt und das Root-Set erneut durchsucht. Diese Phase löst die zweite „Stop-the-world“-Pause im Zyklus aus. Die Anzahl der anstehenden Aktualisierungen und die Größe des Root-Sets bestimmen, wie lange die Pause dauert.
  4. Dann werden in einer weiteren Phase die Objekte aus den in der letzten Markierungsphase identifizierten Regionen kopiert. Dieser Prozess unterscheidet Shenandoah von anderen GCs, da er den Heap parallel zu den Anwendungsthreads aggressiv verdichtet.
  5. Die nächste Phase löst die dritte (und kürzeste) Pause im Zyklus aus. Sie stellt sicher, dass alle GC-Threads die Evakuierung abgeschlossen haben.
  6. Nach Beendigung dieser Phase durchläuft eine weitere Phase den Heap und aktualisiert die Verweise auf Objekte, die zuvor im Zyklus verschoben wurden.
  7. Die letzte „Stop-the-world“-Pause im Zyklus schließt die Aktualisierung der Verweise ab, indem sie die Wurzelmenge aktualisiert. Gleichzeitig werden die evakuierten Regionen wiederhergestellt.
  8. Schließlich werden in der letzten Phase die evakuierten Regionen wiederhergestellt, die nun keine Referenzen mehr enthalten.

Wir können Shenandoah mit einer von drei Heuristiken konfigurieren. Sie bestimmen, wann der GC seine Zyklen beginnt und wie er die Regionen für die Evakuierung auswählt.

1. Adaptiv: Beobachtet die GC-Zyklen und startet den nächsten Zyklus so, dass er abgeschlossen wird, bevor die Anwendung den Heap erschöpft. Diese Heuristik ist der Standardmodus.

2. Statisch: Startet einen GC-Zyklus basierend auf der Belegung des Heaps und dem Zuweisungsdruck.

3. Kompakt: Führt GC-Zyklen kontinuierlich aus. Shenandoah startet einen neuen Zyklus, sobald der vorherige beendet ist, oder basierend auf der Menge des seit dem letzten Zyklus zugewiesenen Heaps. Diese Heuristik führt zu einem Overhead beim Durchsatz, bietet aber die beste Platzrückgewinnung.

Shenandoah muss den Heap schneller sammeln, als die Anwendung, die es bedient, ihn zuweist. Wenn der Allokationsdruck zu hoch ist und nicht genügend Platz für neue Allokationen vorhanden ist, kommt es zu einem Ausfall. Shenandoah verfügt über konfigurierbare Mechanismen für diese Situation.

  • Pacing: Wenn Shenandoah anfängt, hinter der Zuweisungsrate zurückzubleiben, hält es die Zuweisungs-Threads an, um aufzuholen. Die Verzögerungen reichen in der Regel für leichte Zuordnungsspitzen aus. Shenandoah führt Verzögerungen von 10ms oder weniger ein. Wenn das Pacing fehlschlägt, geht Shenandoah zum nächsten Schritt über: degenerierte GC.
  • Degenerierte GC: Wenn ein Allokationsfehler auftritt, startet Shenandoah eine Stop-the-world-Phase. Es nutzt diese Phase, um den aktuellen GC-Zyklus abzuschließen. Da ein Stop-the-World nicht mit der Anwendung um Ressourcen konkurriert, sollte der Zyklus schnell beendet werden und das Zuweisungsdefizit ausgleichen. Oft tritt ein degenerierter Zyklus auf, nachdem der größte Teil der Arbeit des Zyklus bereits abgeschlossen ist, so dass der Stop-the-World kurz ist. Das GC-Protokoll meldet dies jedoch als eine vollständige Pause.
  • Vollständige GC: Wenn sowohl Pacing als auch eine degenerierte GC fehlschlagen, greift Shenandoah auf einen vollständigen GC-Zyklus zurück. Diese letzte GC garantiert, dass die Anwendung nicht mit einem Out-of-Memory-Fehler fehlschlägt, es sei denn, es ist kein Heap mehr vorhanden.

Shenandoah bietet die gleichen Vorteile wie ZGC mit großen Heaps, aber mehr Tuning-Optionen. Je nach der Art Ihrer Anwendung können die verschiedenen Heuristiken gut passen. Seine Pausenzeiten sind vielleicht nicht so kurz wie die von ZGC, aber sie sind besser vorhersehbar.

Um den Shenandoah Garbage Collector zu aktivieren, können wir das folgende Argument verwenden:

java -XX:+UseShenanodoahC -jar Application.java

Abschluss

Der Zweck dieses Artikels ist es, alle Garbage Collectors zusammenzufassen. Daher wurden einige Teile des Inhalts aus den angegebenen Referenzen extrahiert. Wir müssen eine klare Vorstellung über die Garbage Collectors haben, um einen optimalen Garbage Collector für unsere Anwendungsfälle auszuwählen. Der optimale Garbage Collector wird die Leistung unserer Anwendung deutlich verbessern.

Referenz

  1. https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
  2. https://www.baeldung.com/jvm-garbage-collectors
  3. https://dzone.com/articles/java-garbage-collection-3
  4. https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/
  5. https://openjdk.java.net/projects/shenandoah/
  6. https://docs.oracle.com/en/java/javase/12/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0
  7. http://clojure-goes-fast.com/blog/shenandoah-in-production/

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.