Der ultimative Leitfaden zur Java-Leistung – Zusammenfassung 6

Erste Schritte mit der Garbage Collection

Übersicht über die Garbage Collection

GC-Algorithmus

Die JVM bietet die folgenden vier verschiedenen Garbage-Collection-Algorithmen:

  1. Serieller Garbage Collector

Der Serial Garbage Collector ist der einfachste der vier Garbage Collectors. Dies ist auch der Standard-Garbage Collector, wenn die Anwendung auf einer virtuellen Maschine vom Typ Client ausgeführt wird (32-Bit-JVM auf einer Windows-Plattform oder eine JVM, die auf einer Maschine mit einem Prozessor ausgeführt wird).

Der serielle Kollektor bereinigt den Inhalt des Heaps mithilfe eines einzelnen Threads. Wenn Sie den seriellen Kollektor verwenden, werden alle Anwendungsthreads angehalten, wenn der Heap-Speicherplatz bereinigt wird, unabhängig davon, ob Minor GC oder Full GC durchgeführt wird. Bei der Durchführung einer vollständigen GC werden auch Objekte im Raum der alten Generation komprimiert und organisiert. Der serielle Kollektor kann über ein Flag -XX:+UseSerialGCaktiviert werden (in den meisten Fällen ist dieses Flag standardmäßig aktiviert, wenn es verfügbar ist). Beachten Sie, dass im Gegensatz zu den meisten JVM-Flags das Ausschalten des seriellen Kollektors nicht einfach das Pluszeichen in ein Minuszeichen ändert (z. B. use -XX:-UseSerialGC). Wenn Sie auf Systemen, auf denen der serielle Kollektor der Standardkollektor ist, den seriellen Kollektor deaktivieren müssen, können Sie dies tun, indem Sie einen anderen Garbage Collector angeben.

  1. Durchsatz-Garbage Collector

Der Durchsatzkollektor ist der Standardkollektor für virtuelle Maschinen auf Serverebene (Unix-Maschinen mit mehreren CPUs und alle virtuellen 64-Bit-Maschinen).
Der Throughput-Kollektor nutzt Multithreading, um den Speicherplatz der neuen Generation zurückzugewinnen, und der Minor GC ist viel schneller als die Verwendung des Serial-Kollektors. Der Durchsatzkollektor kann bei der Verarbeitung der alten Generation auch Multithreading verwenden. Dies stellt bereits JDK 7u4das Standardverhalten späterer Versionen dar. Für JDK7die virtuellen Maschinen früherer und älterer Versionen -XX:+UseParalleloldGckann diese Funktion über Flags aktiviert werden. Da der Durchsatzkollektor Multithreading verwendet, wird der Durchsatzkollektor oft als Parallelkollektor bezeichnet. Der Durchsatzkollektor unterbricht alle Anwendungsthreads während Minor GC und Full GC und komprimiert und organisiert den Speicherplatz der alten Generation während Full GC. Da es sich in den meisten anwendbaren Szenarien bereits um den Standardkollektor handelt, besteht grundsätzlich keine Notwendigkeit, ihn explizit zu aktivieren. Der Durchsatzkollektor kann bei Bedarf mit dem Flag -XX:+UseParallelGC, aktiviert werden.-XX:+UseParalleloldGc

  1. CMS-Kollektor

Die ursprüngliche Absicht des CMS-Kollektordesigns besteht darin, die langen Pausen im vollständigen GC-Zyklus des Durchsatzkollektors und des seriellen Kollektors zu eliminieren. Der CMS-Kollektor unterbricht alle Anwendungsthreads während der Minor GC und führt die Garbage Collection im Multithread-Verfahren durch. Der bemerkenswerteste Unterschied besteht jedoch darin, dass CMS nicht mehr den Erfassungsalgorithmus von Throughput (-XX:+UseParallelGC) verwendet, sondern einen neuen Algorithmus zum Sammeln von Objekten der neuen Generation (unter Verwendung von-XX:+UseParNewGCFlags) verwendet.

Der CMS-Kollektor unterbricht den Anwendungsthread während der vollständigen GC nicht mehr, sondern verwendet mehrere Hintergrundthreads, um den Speicherplatz der alten Generation regelmäßig zu scannen und Objekte, die nicht mehr verwendet werden, rechtzeitig wiederzuverwenden. Dieser Algorithmus trägt dazu bei, dass CMS ein Kollektor mit geringer Latenz ist: Der Anwendungsthread pausiert während des Minor GC nur extrem kurz und der Hintergrundthread scannt die alte Generation. Die Gesamtdauer der Anwendungs-Thread-Pausen ist viel kürzer als bei Verwendung des Durchsatzkollektors.

Der zu zahlende zusätzliche Preis ist eine höhere CPU-Auslastung: Es müssen genügend CPU-Ressourcen vorhanden sein, um Garbage-Collection-Threads im Hintergrund auszuführen, die die Heap-Nutzung scannen, während Anwendungsthreads ausgeführt werden. Darüber hinaus führt der Hintergrundthread keine Komprimierungsarbeit mehr durch, was dazu führt, dass der Heap nach und nach fragmentiert wird. Wenn die Hintergrundthreads des CMS nicht die CPU-Ressourcen erhalten können, die sie zum Erledigen ihrer Aufgaben benötigen, oder wenn der Heap zu fragmentiert ist, um zusammenhängende Speicherzuweisungsobjekte zu finden, verfällt das CMS in das Verhalten des seriellen Kollektors: Halten Sie alle Anwendungsthreads an und verwenden Sie a Single-Thread-Recycling und Aufräumen des Raums der alten Generation. Dies läuft dann wieder parallel ab und startet den Hintergrundthread erneut (bis der Heap das nächste Mal übermäßig fragmentiert wird). Der CMS-Garbage Collector kann über das Flag -XX:+UseConcMarkSweepGC, aktiviert werden -XX:+UseParNewGC(standardmäßig sind beide Flags deaktiviert).

  1. G1-Garbage Collector

Der G1-Garbage Collector (oder Garbage-First-Collector) ist darauf ausgelegt, Pausen beim Umgang mit sehr großen Heaps (mehr als 4 GB) zu minimieren. Der G1-Sammelalgorithmus unterteilt den Heap in mehrere Regionen (Regionen), gehört aber weiterhin zum Generationssammler. Einige dieser Bereiche umfassen die neue Generation, und die Garbage Collection der neuen Generation verwendet immer noch die Methode, alle Anwendungsthreads anzuhalten, um überlebende Objekte in die alte Generation oder den Survivor-Bereich zu verschieben. Wie andere Erfassungsalgorithmen werden auch diese Vorgänge im Multithread-Verfahren ausgeführt.
Der G1-Kollektor gehört zum Concurrent-Kollektor: Die Garbage-Collection-Arbeit der alten Generation wird vom Hintergrund-Thread erledigt, und für die meisten Arbeiten muss der Anwendungs-Thread nicht angehalten werden. Da das Alter in verschiedene Bereiche unterteilt ist, schließt der G1-Kollektor die Objektbereinigungsarbeit ab, indem er Objekte von einem Bereich in einen anderen kopiert. Dies bedeutet auch, dass der G1-Kollektor während der normalen Verarbeitung eine Heap-Komprimierung implementiert. Aufräumen (zumindest teilweises Aufräumen) . Daher sind Heaps, die den G1-Kollektor verwenden, weniger anfällig für Fragmentierung – obwohl dieses Problem nicht vermieden werden kann.

Wie beim CMS-Kollektor geht die Vermeidung von Full GC mit zusätzlichen CPU-Zyklen einher: Die mehreren Hintergrundthreads, die für die Garbage Collection verantwortlich sind, müssen in der Lage sein, genügend CPU-Zyklen zu erhalten, während die Anwendungsthreads ausgeführt werden. Der G1 Garbage Collector kann mit dem Flag gestartet werden -XX:+UseG1GC(Standard ist aus).

Auslösen und Deaktivieren der expliziten Garbage Collection
Normalerweise wird die Garbage Collection bei Bedarf von der JVM ausgelöst: Minor GC wird ausgelöst, wenn die junge Generation erschöpft ist, Full GC wird ausgelöst, wenn die alte Generation erschöpft ist oder wenn der Heap-Speicherplatz im Begriff ist, gefüllt zu werden . Gleichzeitige Speicherbereinigung auslösen (falls die Situation dies erfordert).

Java bietet auch einen Mechanismus für Anwendungen, um GC zu erzwingen: So geht's System.gc(). Im Allgemeinen ist es keine gute Idee, zu versuchen, GC explizit durch den Aufruf dieser Methode auszulösen. Der Aufruf dieser Methode löst FullGC aus (auch wenn die JVM den CMS- oder G1-Garbage Collector verwendet) und der Anwendungsthread wird für einen beträchtlichen Zeitraum angehalten. Gleichzeitig wird der Aufruf dieser Methode die Anwendung nicht effizienter machen, sondern dazu führen, dass der GC früher startet, aber das verzögert tatsächlich nur die Auswirkungen auf die Leistung später.

Es gibt Ausnahmen von jeder Regel, insbesondere bei der Leistungsüberwachung oder beim Benchmarking. Bei der Ausführung von Benchmarks mit kleinen Codemengen ist es dennoch sinnvoll, vor dem Messzyklus einen GC zu erzwingen, um die JVM schneller aufzuwärmen. Ebenso ist es bei der Heap-Analyse normalerweise eine gute Idee, einen vollständigen GC zu erzwingen, bevor ein Heap-Dump durchgeführt wird. Während die meisten Methoden zum Abrufen eines Heap-Dumps eine vollständige GC durchführen können, gibt es andere Möglichkeiten, eine vollständige GC zu erzwingen: Sie können jcmd<进程号>GC.rundurch Ausführen eine Verbindung zur JVM herstellen oder jconsole verwenden und im Speicherbereich auf die Schaltfläche „GC durchführen“ klicken.

Eine weitere Ausnahme ist RMI, das im Rahmen seines verteilten Garbage Collectors stündlich aufgerufen wird System.gc(). Die Aufrufzeit kann hier über -Dsun.rmi.dgc.server.gcInterval=Nund in den Systemeigenschaften .Dsun.rmi.dgc.cli ent.gcInterval=Ngeändert werden . Die Einheit des N-Werts wird in Millisekunden aufgezeichnet, und Java7der Standardwert (dieser Wert unterscheidet sich auch von der vorherigen Version) beträgt 3600000 (dh eine Stunde).

Wenn das von Ihnen ausgeführte Programm System.gc()versehentlich eine Methode im Code von Drittanbietern aufruft, können Sie diesen GC-Typ explizit über den JVM-Parameter deaktivieren XX:+DisableExplicitGC. Das Flag ist standardmäßig deaktiviert.

kurze Zusammenfassung

  1. Diese vier Garbage-Collection-Algorithmen verwenden unterschiedliche Methoden, um die Auswirkungen von GC auf Anwendungen zu mildern.
  2. 2. Der serielle Kollektor wird häufig verwendet, wenn nur eine einzige CPU verfügbar ist und andere Programme den GC stören (normalerweise der Standardwert).
  3. 3. Der Durchsatzkollektor ist der Standardwert auf anderen virtuellen Maschinen, der den Gesamtdurchsatz der Anwendung maximieren kann, aber bei einigen Vorgängen kann es zu längeren Pausen kommen.
  4. 4. Der CMS-Kollektor kann parallel Müll in der alten Generation sammeln, während der Anwendungsthread ausgeführt wird.
    Wenn die Rechenleistung der CPU ausreicht, um die Ausführung des Hintergrund-Garbage-Collection-Threads zu unterstützen, kann dieser Algorithmus das Auftreten von Full GC in der Anwendung vermeiden.
  5. Der G1-Kollektor kann auch gleichzeitig Müll der alten Generation sammeln, während Anwendungsthreads ausgeführt werden, wodurch das Risiko einer vollständigen GC bis zu einem gewissen Grad verringert werden kann. Das Designkonzept von G1 macht es weniger wahrscheinlich, dass es auf Full GC trifft als auf CMS.

Wählen Sie den GC-Algorithmus aus

Die Wahl des GC-Algorithmus hängt einerseits von den Eigenschaften der Anwendung und andererseits von den Leistungszielen der Anwendung ab.
Der serielle Kollektor eignet sich am besten für Szenarien, in denen die Speichernutzung der Anwendung weniger als 100 MB beträgt. In diesem Fall benötigt die Anwendung nur einen kleinen Heap und weder die parallele Sammlung des Throughput-Kollektors noch die Hintergrundsammlung des CMS-Kollektors oder G1-Kollektors können eine große Rolle spielen.

Dieses Dimensionierungskriterium schränkt auch den Einsatzbereich des seriellen Kollektors ein. Die meisten Programme müssen zwischen Durchsatz- und gleichzeitigen Sammlern wählen. In den meisten Fällen wird die Grundlage für die Auswahl durch die Leistungsziele der Anwendung bestimmt. Verschiedene Anwendungen sind zeitaufwändig, durchsatzmäßig oder durchschnittlich (oder 90 % der Gesamtleistung). Die Anforderungen an die Reaktionszeit sind sehr unterschiedlich.

  1. GC-Algorithmus und Batch-Aufgaben

Bei Batch-Aufgaben sind die durch den Durchsatzkollektor eingeführten Pausen, insbesondere die Pausen des FulI GC, das Hauptproblem. Durch die Ausführung jeder Aufgabe wird ein Teil der Verzögerungszeit (Ablaufzeit) zur Gesamtausführungszeit hinzugefügt. Wenn jeder vollständige GC 0,5 Sekunden dauert und das Programm 20 solcher Zyklen in 5 Minuten ausführt, beträgt der Leistungsverlust bis zu 3,4 %: Ohne diese Pausen kann das Programm in 290 Sekunden statt in 300 Sekunden ausgeführt werden.

Wenn Sie über zusätzliche CPU-Rechenleistung verfügen (was wahrscheinlich ein Problem darstellt), wird die Verwendung des Concurrent-Collectors die Leistung Ihrer Anwendung erheblich verbessern. Der Schlüssel hier ist, ob wir den Threads des Concurrent-Collectors genügend CPU zur Verfügung stellen können, um eine Hintergrundverarbeitung durchzuführen. Um ein einfaches Beispiel zu nennen: Auf einer Maschine mit einer CPU hat eine Single-Thread-Anwendung 100 % der CPU-Ressourcen verbraucht. Wenn die Anwendung mit dem Durchsatzkollektor ausgeführt wurde, kam es zeitweise zu GC, was zum Stillstand der Anwendungsthreads führte. Wenn Sie für dasselbe Programm zum Concurrent-Kollektor wechseln, führt das Betriebssystem den Anwendungsthread für eine Weile auf der CPU und den Hintergrundthread des GC für eine Weile aus. Das Endergebnis ist dasselbe: Der Anwendungsthread bleibt immer noch stehen (wenn auch möglicherweise eine kürzere Pause), während das Betriebssystem andere Threads ausführt.

Dieses Prinzip gilt auch für den allgemeinen Fall, dass mehrere Anwendungsthreads und mehrere Hintergrund-GC-Threads auf einem System mit mehreren CPUs ausgeführt werden. Wenn das Betriebssystem nicht alle Anwendungs-Threads und GC-Hintergrund-Threads gleichzeitig ausführen kann, spiegelt sich die Konkurrenz um die CPU in den Anwendungs-Thread-Störungen wider.
Fügen Sie hier eine Bildbeschreibung ein

Das Bild oben zeigt, wie dieser Kompromiss funktioniert. Batch-Anwendungen, die Bestandsdaten berechnen, wurden in einem Modus ausgeführt, in dem sie Ergebnismengen mehrere Minuten lang im Speicher behalten (um den gesamten Heap aufzufüllen); bei den Tests wurden die CMS- bzw. Throughput-Garbage-Collection-Algorithmen verwendet.

kurze Zusammenfassung

  1. Durch die Verwendung des Durchsatzkollektors zur Verarbeitung von Batch-Aufgaben von Anwendungsthreads kann die Nutzung der CPU-Rechenleistung maximiert und im Allgemeinen eine bessere Leistung erzielt werden.
  2. Wenn die Batch-Aufgaben nicht alle verfügbaren CPU-Ressourcen auf der Maschine nutzen, führt der Wechsel zum Concurrent-Collector oft zu einer besseren Leistung.
  3. Algorithmen und Durchsatztests Wenn der Durchsatz das Ziel einer Testmetrik ist, sind die grundlegenden Kompromisse bei der Auswahl eines GC-Algorithmus dieselben wie bei Batch-Aufgaben, aber die Auswirkung von Pausen ist ganz anders. Die CPU ist immer noch ein sehr wichtiger Teil der Gesamtleistung

Guess you like

Origin blog.csdn.net/weixin_42583701/article/details/131058098