45 Stimmen

Wie man Java Concurrent Mode Fehler und übermäßige gc reduzieren kann

In Java bedeutet das Scheitern des Concurrent-Modus, dass der Concurrent-Collector nicht genügend Speicherplatz für die Tenured- und Permanent-Gen freigeben konnte und daher aufgeben muss und die vollständige stop-the-world gc tritt ein. Das Ergebnis könnte sehr teuer werden.

Ich verstehe dieses Konzept, hatte aber nie ein umfassendes Verständnis von
A) was ein gleichzeitiges Versagen des Modus verursachen könnte und
B) Was ist die Lösung?

Diese Art von Unklarheit führt dazu, dass ich Code schreibe/debugge, ohne viele Hinweise im Hinterkopf zu haben, und oft muss ich diese Performance-Flags von Foo bis Bar ohne besondere Gründe umherschieben, ich muss es einfach versuchen.

Ich würde gerne von den Entwicklern hier erfahren, wie Ihre Erfahrungen sind? Wenn Sie auf ein solches Leistungsproblem gestoßen sind, was war die Ursache und wie haben Sie es gelöst?

Wenn Sie Kodierungsempfehlungen haben, seien Sie bitte nicht zu allgemein. Danke!

24voto

Kevin Lafayette Punkte 241

Das erste, was ich über CMS gelernt habe, ist, dass es mehr Speicher benötigt als die anderen Kollektoren, etwa 25 bis 50 % mehr ist ein guter Ausgangspunkt. Dies hilft Ihnen, Fragmentierung zu vermeiden, da CMS keine Verdichtung vornimmt, wie es die Stop-the-World-Kollektoren tun würden. Zweitens sollte man Dinge tun, die dem Garbage Collector helfen: Integer.valueOf anstelle von new Integer, anonyme Klassen loswerden, sicherstellen, dass innere Klassen nicht auf unzugängliche Dinge zugreifen (private in der äußeren Klasse) und solche Dinge. Je weniger Müll, desto besser. FindBugs und das Nicht-Ignorieren von Warnungen werden dabei sehr helfen.

Was das Tuning angeht, so habe ich festgestellt, dass man mehrere Dinge ausprobieren muss:

-XX:+UseConcMarkSweepGC

Sagt der JVM, dass sie CMS in tenured gen verwenden soll.

Fixieren Sie die Größe Ihres Heaps: -Xmx2048m -Xms2048m Dies verhindert, dass GC Dinge wie das Vergrößern und Verkleinern des Heaps durchführen muss.

-XX:+UseParNewGC

bei der jungen Generation die parallele statt der seriellen Erfassung zu verwenden. Dies beschleunigt Ihre kleineren Sammlungen, insbesondere wenn Sie eine sehr große junge Generation konfiguriert haben. Eine große junge Generation ist im Allgemeinen gut, sollte aber nicht mehr als die Hälfte der alten Generation betragen.

-XX:ParallelCMSThreads=X

legt die Anzahl der Threads fest, die CMS verwenden soll, wenn es Dinge tut, die parallel ausgeführt werden können.

Die Bemerkung -XX:+CMSParallelRemarkEnabled ist standardmäßig seriell, was die Bearbeitung beschleunigen kann.

-XX:+CMSIncrementalMode ermöglicht eine höhere Laufzeit der Anwendung, indem die GC zwischen den Phasen verschoben wird

-XX:+CMSIncrementalPacing ermöglicht es der JVM, die Häufigkeit der Datenerfassung im Laufe der Zeit zu ändern

-XX:CMSIncrementalDutyCycleMin=X Minimale Zeitspanne für GC

-XX:CMSIncrementalDutyCycle=X Beginnen Sie damit, GC in diesem % der Zeit durchzuführen

-XX:CMSIncrementalSafetyFactor=X

Ich habe festgestellt, dass man generell niedrige Pausenzeiten erreichen kann, wenn man es so einrichtet, dass es im Grunde immer sammelt. Da die meiste Arbeit parallel erledigt wird, erhält man im Grunde regelmäßige, vorhersehbare Pausen.

-XX:CMSFullGCsBeforeCompaction=1

Dieser Punkt ist sehr wichtig. Sie sagt dem CMS-Kollektor, dass er die Sammlung immer abschließen soll, bevor er eine neue Sammlung beginnt. Andernfalls kann es passieren, dass er einen Haufen Arbeit wegwirft und neu beginnt.

-XX:+CMSClassUnloadingEnabled

Standardmäßig lässt das CMS Ihr PermGen wachsen, bis es Ihre Anwendung in ein paar Wochen tötet. Dies verhindert dies. Ihr PermGen würde nur wachsen, wenn Sie Reflection verwenden, oder String.intern missbrauchen, oder etwas Schlechtes mit einem Klassenlader tun, oder ein paar andere Dinge.

Man kann auch mit dem Überlebensverhältnis und dem Tenuring-Schwellenwert spielen, je nachdem, ob man langlebige oder kurzlebige Objekte hat und wie viel Objektkopien zwischen den Überlebensräumen man in Kauf nehmen kann. Wenn Sie wissen, dass alle Ihre Objekte langlebig sein werden, können Sie Überlebensräume der Größe Null konfigurieren, und alles, was eine Young-Gen-Sammlung überlebt, wird sofort getendert.

12voto

fglez Punkte 8144

Zitiert von "Verstehen von Concurrent Mark Sweep Garbage Collector Logs"

Der gleichzeitige Ausfall kann entweder vermieden werden, indem die Größe der Tenured Generationsgröße oder die Einleitung der CMS Sammlung bei einer geringeren Heap-Belegung durch Setzen von CMSInitiatingOccupancyFraction zu einer niedrigeren Wert

Wenn Ihre Anwendung jedoch wirklich ein Speicherleck hat, gewinnen Sie nur Zeit.

Wenn Sie einen schnellen Neustart und eine schnelle Wiederherstellung benötigen und einen "schnellen Tod" bevorzugen, würde ich vorschlagen, CMS überhaupt nicht zu verwenden. Ich würde bei "-XX:+UseParallelGC" bleiben.

De "Müllsammler-Ergonomie"

Der parallele Garbage Collector (UseParallelGC) wirft eine Out-of-Memory-Ausnahme, wenn ein übermäßig viel Zeit für das für das Sammeln eines kleinen Teils des Heap. Um diese Ausnahme zu vermeiden, können Sie die Größe des Heaps erhöhen. Sie können auch die Parameter -XX:GCTimeLimit=time-limit y -XX:GCHeapFreeLimit=space-limit

4voto

Stephen C Punkte 665668

Manchmal OOM ziemlich schnell und wurde getötet, manchmal leidet lange gc Zeitraum (letzte Zeit war über 10 Stunden).

Es hört sich für mich so an, als ob ein Speicherleck die Ursache für Ihre Probleme ist.

Ein CMS-Fehler führt nicht zu einem OOM (wie ich es verstehe). Ein CMS-Fehler tritt vielmehr auf, weil die JVM zu viele Sammlungen zu schnell durchführen muss und das CMS nicht mithalten konnte. Eine Situation, in der viele Erfassungszyklen in einem kurzen Zeitraum stattfinden, ist, wenn der Heap fast voll ist.

Die wirklich lange GC-Zeit klingt seltsam ... ist aber theoretisch möglich, wenn Ihr Rechner schrecklich stürzt. Eine lange Periode von wiederholten GCs ist jedoch durchaus plausibel, wenn Ihr Heap fast voll ist.

Sie können die GC so konfigurieren, dass sie aufgibt, wenn der Heap 1) die maximale Größe erreicht hat und 2) nach Abschluss einer vollständigen GC immer noch fast voll ist. Versuchen Sie dies, wenn Sie es nicht schon getan haben. Es wird Ihre Probleme nicht beheben, aber zumindest wird Ihre JVM den OOM schnell erkennen, was einen schnelleren Neustart des Dienstes und eine schnellere Wiederherstellung ermöglicht.

EDIT - Die Option dazu lautet -XX:GCHeapFreeLimit=nnn wobei nnn ist eine Zahl zwischen 0 und 100, die den Mindestprozentsatz des Heaps angibt, der nach der GC frei sein muss. Die Voreinstellung ist 2. Die Option ist in dem treffend benannten "Die vollständigste Liste von -XX Optionen für Java 6 JVM" Seite. (Dort sind viele -XX-Optionen aufgeführt, die in der Sun-Dokumentation nicht erscheinen. Leider enthält die Seite nur wenige Details darüber, was die Optionen tatsächlich tun).

Sie sollten wahrscheinlich beginnen, um zu sehen, ob Ihre Anwendung / Webapp Speicherlecks hat. Wenn dies der Fall ist, werden Ihre Probleme nicht verschwinden, bis diese Lecks gefunden und behoben sind. Langfristig wird das Herumfummeln an den Hotspot GC-Optionen die Speicherlecks nicht beheben.

0voto

dave Punkte 1

Ich habe festgestellt, dass die Verwendung von -XX:PretenureSizeThreshold=1m um ein "großes" Objekt sofort in einen festen Speicherplatz zu verschieben, hat meine jungen GC- und Concurrent-Modus-Fehler stark reduziert, da es dazu neigt, nicht zu versuchen, die junge + 1 überlebende Menge an Daten zu löschen ( xmn=1536m survivorratio=3 maxTenuringThreashould=5 ), bevor ein vollständiger CMS-Zyklus abgeschlossen werden kann. Ja, mein Überlebensraum ist groß, aber etwa einmal alle 2 Tage kommt etwas in die App, das ihn braucht (und wir betreiben 12 App-Server pro Tag für eine App).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X