Garbage Collection Bootcamp 1.0
Inhaltsverzeichnis
Was ist Garbage Collection
Bei der Garbage Collection in Java geht es um die Freigabe des dynamischen Speichers von Objekten, die nicht mehr von einer Anwendung verwendet werden. In Sprachen wie C oder C++ ist der Entwickler oft selbst für die Verwaltung des dynamischen Speichers verantwortlich (mit malloc und free oder new und delete). In Java wird diese Aufgabe jedoch dem so genannten Garbage Collector überlassen. Ein Garbage Collector gibt ungenutzten Speicher automatisch frei und befreit den Entwickler so von einem Großteil dieser undankbaren Speicherjonglage.
Der grundlegendste Garbage Collection-Algorithmus beginnt mit den Root-Objekten (d.h. Objekten auf dem Thread-Stack, statischen Objekten usw.), die live sind (live bedeutet, dass sie gerade verwendet werden), und iteriert dann über jedes erreichbare Objekt. Jedes Objekt, das auf diese Weise nicht erreicht werden kann, ist Müll und kann eingesammelt werden. Die Anwendung wird angehalten, während dieser Prozess abläuft. Dies wird als Markieren und Durchsuchen bezeichnet – zuerst markieren Sie die Objekte, die aktiv sind, dann durchsuchen Sie die, die nicht aktiv sind. Die dafür benötigte Zeit ist natürlich proportional zur Anzahl der aktiven Objekte (die in modernen Java-Anwendungen recht groß sein kann), so dass effizientere Sammelverfahren entwickelt wurden.
Ein solches Schema ergibt sich aus der natürlichen Tatsache, dass Sie Objekte nach ihrer Lebensdauer aufteilen können. Die meisten Anwendungen erstellen eine Menge sehr kurzlebiger Objekte und weniger Objekte, die lange Zeit bestehen (ich habe Schätzungen gesehen, dass bei einer durchschnittlichen Anwendung 85-98% der zugewiesenen Objekte kurzlebig sind). Diese Tatsache können Sie sich bei der Erstellung von Sammlungen zunutze machen. In Java werden Objekte aus einem Bereich des Speichers zugewiesen, der als Heap bezeichnet wird. Der Java-Heap ist in der Regel in mehrere Bereiche unterteilt (in der Regel ist er bei allen Implementierungen gleich, aber es gibt auch die eine oder andere Ausnahme). Die wichtigsten Bereiche sind die junge Generation, die dauerhafte Generation (auch alte Generation genannt) und die permanente Generation. Die junge Generation ist dann weiter unterteilt in den Eden-Raum und zwei Überlebensräume. Die permanente Generation ist in der Regel für Objekte gedacht, die für die gesamte Lebensdauer der Anwendung vorhanden sind (internierte Strings, Klassenobjekte usw.) und spielt in der Regel keine große Rolle bei der Garbage Collection. Die Größe der permanenten Generierung ist nicht Teil der mit -Xms und -Xmx definierten Heap-Region. Obwohl dies ein sehr ungewöhnlicher Bedarf ist, ist es dennoch erwähnenswert, dass die permanente Generierung bei Bedarf tatsächlich gesammelt werden kann:
-XX:+CMSPermGenSweepingEnabled
Wenn Objekte zum ersten Mal erstellt werden, werden sie dem Eden-Bereich zugewiesen. Wenn der Eden-Space voll ist, werden die noch lebenden Objekte in einen der Survivor-Spaces kopiert (oder, wenn sie nicht mehr hineinpassen, in den Tenured-Space). Ein Überlebensraum bleibt immer leer, und bei jeder Sammlung der jungen Generation (einer kleineren Sammlung) werden die lebenden Objekte aus dem Edenraum und dem nicht leeren Überlebensraum in den leeren Überlebensraum kopiert. Auf diese Weise bleibt ein neu geleerter Überlebensraum für die nächste Runde übrig, da alle noch lebenden Objekte aus dem ehemals vollen Überlebensraum in den geleerten Raum kopiert werden.
Wie Sie sehen, müssen Sie jetzt nicht mehr jedes Objekt bei jeder Sammlung durchgehen, sondern können die jungen Generationen häufiger und die langlebigen Generationen (Objekte mit langer Lebensdauer) viel seltener sammeln. Sie können Ihre Sammlung auch für die Eigenschaften des Raums optimieren – d.h. in der Regel werden fast alle Objekte im jungen Raum Müll sein. Im Allgemeinen muss ein Objekt ein paar kleinere Sammlungen überleben, um in den Tenured Space zu gelangen (zuerst in den Survivor Space und dann in den Tenured Space). Ein Kopierkollektor identifiziert Datenmüll, indem er lebende Objekte von einem Bereich in einen anderen kopiert – alles, was übrig bleibt, ist per Definition Datenmüll. Das Sun JDK verwendet Kopierkollektoren für den Young Space und Mark- und Sweep-Typ-Kollektoren für den Tenured Space.
Tuning der Garbage Collection
Tuning für die Garbage Collection bedeutet, dass Sie die Größe der verschiedenen im vorigen Abschnitt erwähnten Bereiche sowie die Algorithmen, die zum Sammeln dieser Bereiche verwendet werden, anpassen. Sie können dies mit verschiedenen JVM-Befehlszeilenoptionen tun.
Die Menge an RAM, die für die verschiedenen Bereiche verfügbar ist, hängt von der Größe des Heaps ab, den die JVM zugewiesen hat. Die Standardeinstellungen werden auf der Grundlage der erkannten Hardware gewählt, aber Sie können in der Regel besser abschneiden, wenn Sie selbst ein gutes Xms, Xmx angeben. Auf einem Server-Rechner kann es sinnvoll sein, diese beiden Einstellungen zusammenzulegen, damit die JVM keine Zeit mit der Größenanpassung verschwendet. Im Allgemeinen möchten Sie den Heap nicht viel größer als nötig machen – dies kann die Kosten für vollständige Garbage Collections unnötig in die Höhe treiben und Arbeitsspeicher von anderen wichtigen Aktivitäten, wie z.B. dem Caching des Dateisystems, abziehen.
-Xms |
Ursprüngliche Heap-Größe |
-Xmx |
Maximale Heap-Größe |
Ein Hinweis zu den JVM Cmd-Zeilenoptionen
- Boolesche Optionen – Ein:
-XX:+<option>
Aus:-XX:-<option>
. - Numerische Optionen:
-XX:<option>=<number>
. Zahlen können ‚m‘ oder ‚M‘ für Megabytes, ‚k‘ oder ‚K‘ für Kilobytes und ‚g‘ oder ‚G‘ für Gigabytes enthalten (1M= 1048576). Im Falle von Xms und Xmx wird nur ein X und kein Doppelpunkt verwendet. - String-Optionen:
-XX:<option>=<string>
Dimensionierung der einzelnen Räume
In der Regel sollten Sie der jungen Generation viel Speicherplatz zugestehen – vor allem, wenn Sie mehrere Prozessoren haben -, da die Zuweisung parallelisiert werden kann und jeder Thread seinen eigenen privaten Teil des Eden-Speicherplatzes zur Verfügung hat. Im Allgemeinen sollten Sie der jungen Generation jedoch weniger als die Hälfte des Speicherplatzes der etablierten Generation zugestehen – vor allem, wenn Sie den Serialized Collector verwenden. Etwa 33% ist normalerweise ein guter Wert für den Anfang. Die optimale Größe ist von Anwendung zu Anwendung unterschiedlich und hängt von der Verteilung der jungen und langlebigen Objekte ab. Sie wollen nicht, dass der Young Space so klein ist, dass sich viele kurzlebige Objekte im Tenured Space stapeln. Sie wollen auch nicht, dass er so groß ist, dass der Tenured Space nicht mehr genug Platz hat und/oder die Sammlungen der jungen Generation zu lange brauchen, um abgeschlossen zu werden.
Neben der Dimensionierung des gesamten Haufens kann die Dimensionierung der neuen Generation (ein anderer Name für die junge Generation) das wichtigste Element für eine gute Leistung sein.
-XX:NewSize |
(Seit 5.0) Größe der jungen Generation beim Start der JVM – diese wird automatisch berechnet, wenn Sie NewRatio angeben |
-XX:MaxNewSize |
(Seit 1.4) Die größte Größe, auf die die junge Generation anwachsen kann (unbegrenzt, wenn nicht angegeben) |
-Xmn |
Legt die neue Generation auf eine feste Größe fest – dies ist normalerweise nicht empfehlenswert, es sei denn, Sie legen auch die anderen Speichergrößen fest. |
-XX:NewRatio |
Legt die Größe der neuen Generation im Verhältnis zur Größe der alten Generation fest. |
-XX:SurvivorRatio |
Sie können auch die Größe der überlebenden Leerzeichen einstellen – in der Praxis ist dies jedoch meist nicht sehr hilfreich. |
Die beste Größe wird in der Regel durch Spielen mit den Parametern und anschließendes Testen der Leistung Ihrer Anwendung ermittelt. Oft verwendet die JVM gute Standardwerte oder passt die Größe der Speicherbereiche je nach verwendetem Garbage Collector auf der Grundlage historischer Statistiken selbst an.
Es gibt ein paar hilfreiche Tools, die Ihnen einen Einblick in den Garbage Collection-Prozess geben.
Ein Blick in die Garbage Collection
Sie können die folgenden Befehlszeilenoptionen verwenden, um Informationen über den Garbage Collection-Prozess zu generieren:
-verbose:gc |
Drucken Sie Informationen über Heap und gc auf jeder Sammlung. |
-XX:+PrintGCDetails |
(Seit 1.4) Druckt zusätzliche Informationen zur Garbage Collection. |
-XX:+PrintGCTimeStamps |
(Seit 1.4) Hinzufügen von Zeitstempeln zu den Garbage Collection Protokollen. |
-Xloggc:C:whereevergc.log |
Geben Sie die Protokolldatei an. |
Es gibt verschiedene Tools, mit denen Sie diese Protokolle entschlüsseln können. Eines davon ist GCViewer – allerdings kann es nur gc-Protokolle bis Java 5.0 lesen (obwohl es teilweise 6.0-Dateien lesen kann). Eine weitere gute Option von IBM ist PMAT, das auch Java 6 gc-Protokolle lesen kann.
Es gibt auch ein sehr cooles Tool namens VisualGC, mit dem Sie visuell beobachten können, wie sich Objekte zwischen Räumen in Echtzeit bewegen, während Ihre Anwendung läuft. Es ist als eigenständige Anwendung oder als Plugin für Netbeans und VisualVM erhältlich.
Die Müllsammler
Das Folgende gilt sowohl für die Sun Java-Implementierung als auch für OpenJDK.
Es gibt drei Hauptschemata für die Garbage Collection, mit denen Sie sich befassen sollten (vieles davon gilt für Java 1.4, aber im Allgemeinen richte ich mich an Java 1.5 und höher). Diese Schemata werden oft selbst als Collectors bezeichnet, aber im Allgemeinen umfasst jedes zwei Collectors – einen für den alten und einen für den neuen Speicherbereich. Diese Collector-Schemata werden oft mit den Namen der Collectors für den alten Bereich bezeichnet: der Serialized Collector, der Throughput Collector und der Concurrent Low Pause Collector.
Es gibt auch einen älteren inkrementellen Collector (der nicht unterstützt wird und auch als Zug-Collector bezeichnet wird) und einen inkrementellen Sammelmodus für den gleichzeitigen Low-Pause-Collector (den ich anspreche und der im Allgemeinen verwendet wird, wenn nur eine oder zwei CPUs zur Verfügung stehen), aber ich überlasse es Ihnen, diese selbst zu erforschen, wenn Sie daran interessiert sind.
Cmd Zeile Arg |
-XX:+UseSerialGC (Since 5.0) |
Neuer Space Collector | Seriell – Single-Thread, hält die Welt an, kopiert Kollektor |
Old Space Sammler | Serial Old – einfädiger, die Welt anhaltender, markierungsfreier, kompakter Kollektor |
Mit dem serialisierten Collector wird eine große Sammlung durchgeführt, wenn der Tenure-Speicher voll ist. Dies wird als „stop the world“-Abholung bezeichnet, da alle Anwendungs-Threads angehalten werden, während die Abholung stattfindet.
Dieser Collector eignet sich am besten für kleine Anwendungen, Anwendungen, die auf einem Rechner mit nur einer CPU laufen, oder Anwendungen, bei denen die Pausenzeiten keine Rolle spielen. Dieser Collector ist relativ effizient, da er nicht zwischen Threads kommunizieren muss, aber Sie müssen bereit sein, die Pausen, in denen die Welt angehalten wird, zu akzeptieren. Kleinere Sammlungen halten ebenfalls „die Welt an“, sind aber im Allgemeinen recht effizient und schnell.
Dieser Collector ist der einzige, den ich gesehen habe, der -XX:MaxHeapFreeRatio respektiert – obwohl dies nur geschieht, wenn eine vollständige Sammlung ausgelöst wird. Wenn Sie versuchen, Ihren RAM-Bedarf so gering wie möglich zu halten und immer so viel Speicher wie möglich an das Betriebssystem zurückzugeben, kann die Verwendung des serialisierten Collectors und einer aggressiven -XX:MaxHeapFreeRatio eine gute Strategie sein. Sie können gelegentlich eine vollständige Sammlung mit System.gc() erzwingen, wenn Ihre Anwendung im Leerlauf ist.
Der Durchsatzkollektor (auch bekannt als Parallelkollektor)
Cmd Zeile Arg |
-XX:+UseParallelGC (Since 1.4.1) |
Neuer Space Collector | Parallel Scavenge – Multithreading, Welt anhalten, Kopieren von Sammlern |
Old Space Sammler | Serial Old – einfädiger, die Welt anhaltender, markierungsfreier, kompakter Kollektor |
Der Kollektor für den Durchsatz verwendet eine parallele Version des Kollektors für die junge Generation, während der Kollektor für die alte Generation weiterhin den seriellen Kollektor verwendet. Während also ein einzelner Thread weiterhin die Sammlungen für den Tenured Space durchführt, arbeiten mehrere Threads zusammen, um den Young Space zu sammeln.
In Java 1.5 Update 6 wurde eine Funktion namens parallele Verdichtung hinzugefügt – diese Funktion ermöglicht es dem Throughput Collector, auch große Sammlungen parallel durchzuführen. Sie können dies mit -XX:+UseParallelOldGC aktivieren. Dies sollte sich sehr positiv auf die Skalierbarkeit auswirken, da Sie so den Engpass eines einzelnen Sammelthreads bei sehr großen Heaps (mehrere Gigabyte) umgehen können. Ich habe gelesen, dass dies die Leistung bei kleineren Heaps aufgrund von Sperrkonflikten verringern kann.
Der Durchsatzkollektor sollte der Standardkollektor sein, der auf Rechnern der Serverklasse (in Java 1.5 und höher) gewählt wird, aber es gibt Ausnahmen – auf meinem MacbookPro ist beispielsweise der CMS-Kollektor voreingestellt. Sie können diese Voreinstellungen jederzeit außer Kraft setzen.
Der Durchsatz ist in der Regel am nützlichsten, wenn Ihre Anwendung eine große Anzahl von Threads hat, die neue Objekte erstellen, und Sie mehr als einen Prozessor zur Verfügung haben (am besten jedoch mehr als zwei). Wenn Sie mehrere Threads haben, die Objekte zuweisen, wollen Sie in der Regel auch die Größe der jungen Generation erhöhen. Die Anzahl der Garbage Collector-Threads entspricht in der Regel der Anzahl der Prozessoren, die Sie haben, aber Sie können diese Anzahl mit -XX:ParallelGCThreads=n steuern. Manchmal möchten Sie die Anzahl der Threads verringern, da jeder Thread einen Teil der Tenured Generation für Promotionen reserviert. Dies kann zu einem Fragmentierungseffekt führen und die Größe der Tenured Generation effektiv verringern (dies ist im Allgemeinen nur ein Problem, wenn Ihre Anwendung Zugriff auf viele Prozessoren oder Kerne hat).
Der Durchsatzsammler unterstützt auch die so genannte Ergonomie. Im Rahmen dieser Unterstützung können Sie verschiedene gewünschte Verhaltensweisen für Ihre Anwendung angeben, und die JVM wird versuchen, verschiedene Einstellungen zu optimieren, um Ihre Ziele zu erreichen.
-XX:MaxGCPauseMillis=n ein Hinweis für den Durchsatzkollektor, dass eine maximale Pausenzeit von n Millisekunden erwünscht ist. In der Voreinstellung gibt es keinen Hinweis. Der Collector passt die Heap-Größe und andere Sammelparameter an, um das Ziel zu erreichen. Beachten Sie, dass der Durchsatz bei dem Versuch, dieses Ziel zu erreichen, beeinträchtigt werden kann. Es gibt auch keine Garantie, dass das Ziel erreicht wird.
Sie können mit -XX:GCTimeRatio auch ein Ziel festlegen, wie viel Zeit im Vergleich zur Ausführung Ihrer Anwendung für die Garbage Collection aufgewendet wird. Standardmäßig ist dies auf 1% eingestellt (beachten Sie, dass sich diese Standardwerte von Version zu Version ändern können).
Mit dem serialisierten Garbage Collector wird eine Generation eingesammelt, wenn sie voll ist (d.h. wenn keine weiteren Zuweisungen aus dieser Generation vorgenommen werden können). Dies gilt auch für den Durchsatzsammler.
Der Sammler für gleichzeitige niedrige Pausen
Cmd Zeile Arg |
-XX:+UseConcMarkSweepGC (Since 1.4.1) |
Neuer Raumkollektor | Par New – Multi-Thread-Kollektor, der die Welt anhält und mit CMS arbeitet. |
Old Space Kollektor | Normalerweise CMS, der meist nebenläufige Low-Pause-Collector – es sei denn, es gibt einen Ausfall im Nebenläufigkeitsmodus, in diesem Fall Serial Old – single threaded, stop the world, mark-sweep-compact collector |
Verwenden Sie den gleichzeitigen Low-Pause-Collector, wenn Sie es sich leisten können, die Prozessorressourcen mit dem Garbage Collector zu teilen, während die Anwendung läuft. Dies ist in der Regel gut für eine Anwendung mit vielen langlebigen Daten – was bedeutet, dass Sie einen großen, dauerhaften Generierungsbereich benötigen. Natürlich ist es auch hilfreich, mehrere Prozessoren zu haben. Dieser Kollektor hält die Anwendungs-Threads während einer Sammlung zweimal an – einmal kurz zu Beginn (wenn er Objekte markiert, auf die von Root-Objekten aus direkt zugegriffen werden kann) und eine etwas längere Pause zur Mitte hin (wenn er nach den Objekten sucht, die er aufgrund der parallelen Markierung übersehen hat) – der Rest der Sammlung wird gleichzeitig mit einem der verfügbaren Prozessoren (oder einem Thread) durchgeführt. Wenn dieser Collector die Sammlung nicht abschließen kann, bevor er voll ist, werden alle Threads angehalten und eine vollständige Sammlung durchgeführt. Dies wird als Fehler im gleichzeitigen Modus bezeichnet und bedeutet wahrscheinlich, dass Sie die Parameter für die gleichzeitige Sammlung anpassen müssen.
Dieser Collector wird für die Tenured Generation verwendet und führt die Sammlung gleichzeitig mit der Ausführung der Anwendung durch. Dieser Kollektor kann auch mit einer parallelen Version des Kollektors für die junge Generation gekoppelt werden (-XX:+UseParNewGC).
Beachten Sie, dass -XX:+UseParallelGC (der Durchsatzkollektor) nicht mit -XX:+UseConcMarkSweepGC verwendet werden sollte. Die JVM wird beim Start fehlschlagen, wenn Sie dies mit den meisten modernen JVMs versuchen. Dasselbe gilt für -XX:+UseParallelOldGC.
Der Sammler für gleichzeitige niedrige Pausen führt Statistiken, so dass er am besten abschätzen kann, wann er mit dem Sammeln beginnt (so dass er fertig ist, bevor der Speicherplatz voll ist). Er beginnt auch mit dem Sammeln, wenn der Speicherplatz einen bestimmten Prozentsatz des verfügbaren Speicherplatzes erreicht hat – Sie können dies manuell mit -XX:CMSInitiatingOccupancyFraction=n einstellen. Der Standardwert für diese Einstellung variiert von JVM zu JVM. Ich habe gelesen, dass der Standardwert für 1.5 68% war, während der Standardwert für 1.6 92% ist. Sie können diesen Wert bei Bedarf herabsetzen, um sicherzustellen, dass die Sammlung früher beginnt. Dann ist es wahrscheinlicher, dass Sie die Sammlung beenden, bevor der Speicherplatz voll ist.
Der gleichzeitige Low-Pause-Collector kann auch in einem inkrementellen Modus verwendet werden, auf den ich hier nicht näher eingehen werde. In diesem Modus gibt der Low-Pause-Collector den Prozessor, der für die parallele Sammlung verwendet wird, gelegentlich an die Anwendung zurück und verringert so seine Auswirkungen auf die Anwendungsleistung.
Der parallele Sammler der jungen Generation
-XX:+VerwendungParNewGC
Dieser Kollektor ähnelt dem Durchsatzkollektor insofern, als er die junge Generation parallel sammelt. Das meiste, was für den Durchsatzkollektor gilt, gilt auch für diesen Kollektor. Allerdings wird eine andere Implementierung verwendet, die es diesem Kollektor im Gegensatz zum Durchsatzkollektor ermöglicht, mit dem gleichzeitigen Low-Pause-Kollektor zusammenzuarbeiten. Obwohl einige Sun/Oracle-Literatur darauf hinweist, dass diese Funktion standardmäßig deaktiviert ist, scheint sie bei der Verwendung von CMS mindestens in Java 6 standardmäßig aktiviert zu sein. Sie können es mit deaktivieren:
-XX:+UseConcMarkSweepGC -XX:-UseParNewGC
Die Kehrseite der Medaille ist, dass der Garbage Collector mit Durchsatz (-XX:+UseParallelGC) zwar mit adaptiver Größenbestimmung (-XX:+UseAdaptiveSizePolicy) verwendet werden kann, der parallele Young Generation Collector (-XX:+UseParNewGC) jedoch nicht.
-XX:+UseAdaptiveSizePolicy zeichnet Statistiken über GC-Zeiten, Allokationsraten und freien Speicherplatz auf und passt dann die Größe der jungen und dauerhaften Generationen an diese Statistiken an. Diese Option ist für die Verwendung mit dem Durchsatzkollektor vorgesehen und standardmäßig aktiviert.
Auswahl eines Sammlers
Hinweis: Dieser Artikel befasst sich mit Serveranwendungen und dem -server hotspot vm.
Normalerweise sollten Sie mit dem parallelen Collector (Durchsatz) beginnen. Er ist der ergonomischste und passt die wichtigsten Einstellungen automatisch an, so dass die meisten Serveranwendungen gut damit zurechtkommen. Dies ist der Standardkollektor auf den meisten Systemen der Serverklasse. Im Allgemeinen müssen Sie die Einstellungen für die Garbage Collection erst dann ändern, wenn Sie festgestellt haben, dass Sie ein Problem mit der Garbage Collection zu lösen haben.
Wenn Sie es mit sehr großen Heaps zu tun haben, kann der Parallel Collector versagen – er sammelt den Tenured Space mit einer Stop-the-World-Collection, was bedeutet, dass Ihre App eingefroren ist, während die Sammlungen stattfinden. Wenn Sie also feststellen, dass der Parallel-Collector nicht ausreicht, selbst wenn Sie UseParallelOldGC verwenden, können Sie den meist gleichzeitigen Low-Pause-Collector ausprobieren. Dieser Collector sammelt die Daten, während Ihre Anwendung mit einem Thread nebenher läuft, mit zwei viel kürzeren Stop-the-World-Pausen. Insgesamt ist der CMS-Collector in Bezug auf den Durchsatz langsamer – aber Ihre Anwendung wird wahrscheinlich seltener eingefroren.
Die Ergonomie spielt hier keine Rolle. Sie sind also auf sich allein gestellt, wenn es darum geht, gute Einstellungen zu finden, falls sich die Standardeinstellungen nicht als passend erweisen – aber Sie können mit diesem Collector oft lange „die Welt steht still“-Pausen beseitigen.
Die Hoffnung ist, dass es einfach sinnvoll ist, in Zukunft immer den G1-Kollektor zu verwenden – er versucht, das Beste aus beiden Welten des Durchsatzes und der meist gleichzeitigen Low-Pause-Kollektoren zu bieten.
Der Müllsammler G1 (Garbage First)
Der Garbage First Collector ist ein neuer Garbage Collector, der alle anderen übertreffen soll. Er ist in Sun Java 6 Update 14 sowie in aktuellen Versionen von OpenJDK6 und frühen Versionen von OpenJDK 7 verfügbar. Irgendwann werde ich mehr über diesen Kollektor schreiben. Kurz gesagt: Der G1-Collector soll das Beste aus den beiden Kollektoren mit hohem Durchsatz und meist gleichzeitiger niedriger Pause vereinen. Er verwendet neue Strategien, um Stop-the-World-Pausen zu minimieren und einen hohen Durchsatz auf Multiprozessorsystemen mit sehr großen Heaps zu gewährleisten.
Versuchen Sie diesen Sammler mit:
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC