Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Garbage Collection Tuning ? Die Details

Garbage Collection Tuning – Die Details
Memory Management 
Garbage Collection Tuning – Die Details 
 
 

Java Magazin, Dezember 2010
Klaus Kreft & Angelika Langer

Dies ist das Manuskript eines Artikels, der im Rahmen einer Kolumne mit dem Titel "Effective Java" im Java Magazin erschienen ist.  Die übrigen Artikel dieser Serie sind ebenfalls verfügbar ( click here ).

Nachdem wir in unserem letzten Beitrag (/ GC5 /) die Garbage-Collection-Tuningziele „hoher Durchsatz“, „konstant kurze Pausen“ und „geringer Speicherverbrauch“ diskutiert haben, wollen wir uns in diesem Artikel mit dem eigentlich Tuning beschäftigen.  Bei den aktuellen Sun-JVMs mit ihrer Generational Garbage Collection ist der erste Schritt beim Tuning die Auswahl des richtigen Garbage Collectors für Young und Old Generation.  Der zweite Schritt besteht dann darin, die ausgewählten Garbage Collectoren an die Objekt-Population der Applikation anzupassen.
 

Auswahl des Garbage Collectors

Vor dem eigentlichen Tuning ist es wichtig, dass für Young und Old Generation die jeweils „richtigen“ Garbage Collectoren genutzt werden.  „Richtig“ hängt dabei im Wesentlichen von zwei Kriterien ab:
  • der Hardware, auf der die JVM und damit die Applikation läuft, und
  • dem Tuningziel, also entweder „hoher Durchsatz“ oder „konstant kurze Pausen“.
Beim Thema Hardware geht es allein um die Anzahl der Cores , die auf der Plattform vorhanden sind und damit potentiell dem Garbage Collector zur Verfügung stehen.  Man muss entscheiden, ob sich die Benutzung eines parallelen Garbage Collectors lohnt. Unter paralleler Garbage Collection versteht man einen Garbage Collector, der seine Arbeit auf mehrere parallele Threads aufteilt.  Wir haben diesen Aspekt für die Young Generation in / GC2 / und für die Old Generation in / GC3 / ausführlich diskutiert.

Die Entscheidung ist relativ einfach, wenn nur ein Core zur Verfügung steht.  Für die Young Generation gibt es dann zum Serial Young Garbage Collector (siehe / GC2 /) keine Alternative.  Ist hoher Durchsatz das Tuningziel, so ist auch der Serial Old Garbage Collector (siehe / GC3 /) alternativlos.  Kommt es einem dagegen auf konstant kurze Pausen an, so wählt man für die Old Generation den Concurrent-Mark-And-Sweep (CMS) Garbage Collector (siehe / GC4 /).

Bei einer Plattform mit zwei bis drei Cores lässt sich im Allgemeinen nicht im Voraus absehen, ob ein serieller oder ein paralleler Garbage Collector auf der Young bzw. Old Generation die richtige Lösung ist. Der Vorteil eines parallelen Garbage Collectors ist, dass er die vorhandenen mehreren Cores nutzen kann.  Auf der andern Seite entsteht aber wegen der parallelen Garbage Collector Threads ein Synchronisationsoverhead, der den vorher beschriebenen Vorteil möglicherweise vollständig zunichte machen oder sogar umkehren kann.  Um im konkreten Fall zu entscheiden, welches nun die bessere Alternative ist, kommt man um einen Praxistest nicht herum.  Dafür wird man die Applikation mit jeder Garbage-Collector-Konfiguration unter realistischen Lastanforderungen laufen lassen und dabei einen Trace erzeugen (mit der JVM-Option –verbose:gc ).  Die Traces kann man dann bezüglich des Tuningziels (konstant kurze Pausenzeiten) vergleichen und so die konkret passende Konfiguration wählen.

Ab vier Cores kann man sich im Allgemeinen sicher sein, dass parallele Garbage Collectoren seriellen vorzuziehen sind.  Das bedeutet, dass auf der Young Generation nun der Parallel Young Garbage Collector (siehe / GC2 /) konkurrenzlos ist.  Ist hoher Durchsatz das Tuningziel, so gilt dies bezüglich der Old Generation auch für den Parallel Old Garbage Collector.  Sind konstant kurze Pausen das Tuningziel, so ist der CMS Garbage Collector die richtige Wahl.

Die oben beschriebenen Auswahlkriterien für den Garbage Collector auf Young bzw. Old Generation finden sich zusammengefasst in Abbildung 1 wieder.  Dort sind auch die JVM-Optionen angegeben, um den jeweiligen Garbage Collector zu aktivieren.

Abbildung 1: Auswahl der Garbage Collectoren


Anpassen der Collectoren an die Objekt-Population der Applikation

Die Auswahl des Garbage Collectors für Young und Old Generation ist nur der erste Schritt des Tunings.  Man kann ihn als das Grobtuning betrachten.  Im nächsten Schritt geht es darum, die ausgewählten Garbage Collectoren ihrerseits an die spezifische Objekt-Population der Applikation anzupassen.  Das ist dann das Feintuning. Bevor wir uns das Anpassen der Garbage Collectoren genauer ansehen, vorab ein paar Überlegung zur Objekt-Population der Applikation und der sich daraus ergebenden Ausnutzung der Speicher-Generationen des Garbage Collectors.

Objekt-Population der Applikation und Generationenauslastung

Mit Objekt-Population der Applikation ist gemeint:  Wie viele Objekte werden neu angelegt?  Wie viel Speicher wird dafür benötigt?  Wie lange leben diese Objekte?  Dabei will man herausfinden, wie die Applikation den Speicher nutzt und wie die Garbage Collection darauf reagieren muss.  In unserem Artikel zur Generational Garbage Collection (/ GC1 /) hatten wir die Abbildung 2 vorgestellt, die die typische Objekt-Population einer Java-Anwendung zeigt.


Abbildung 2: Typische Objekt-Population

Hier sieht man, dass relativ viele Objekte nur sehr kurz leben.  Das sind die Objekte, die nur für eine Methode oder sogar nur für eine Expression gebraucht werden.  Von den Objekten mittlerer Lebensdauer gibt es deutlich weniger.  Beispiele dafür sind Objekte, die den Session State von Servlets oder Services repräsentieren.  Noch mal deutlich weniger Objekte sind wirklich langlebig.  Dies sind typischerweise Objekte, die die Infrastruktur der Applikation bilden: Thread Pools, zentrale Objekte von Frameworks, usw. .

Für das Feintuning ist es wichtig, ein Gefühl für die Objekt-Population der zu tunenden Applikation zu bekommen.  Denn Garbage-Collection-Tuning bedeutet: wie lassen sich die Objekte mit ihrer unterschiedlichen Lebensdauer am besten abbilden auf die verschiedenen Bereiche der Generational Garbage Collection, d.h. auf die Young Generation (bestehend aus Eden und zwei Survivor Spaces) und die Old Generation.  Deshalb geht es darum, bei der Applikation ein Gefühl dafür zu bekommen, wie viel Objekte Eden überleben.  Überleben diese Objekte dann auch noch die Survivor Spaces und werden in die Old Generation kopiert?  Oder sind die Survivor Spaces zu klein und sie werden gleich bei der ersten Minor Collection in die Old Generation kopiert?  Vielleicht sterben sie aber auch in den Survior Spaces.

Um die Abbildung der Objekt-Population auf die Garbage Collector Generationen (Generationenauslastung) zu ermitteln, benutzt man Werkzeuge.  Solche Tools finden sich im jvmstat-Paket auf Suns Developer Network (/ JSTT /). Ein Beispiel daraus ist jstat. Dieses Tool ermittelt periodisch Statistikdaten aus der JVM. Auf Grund seiner vielfältigen Optionen ist es äußerst mächtig und flexibel.  Abbildung 3 zeigt zum Beispiel die Ausgabe von jstat in einer Konfiguration, bei der die aktuelle Größen und Auslastungen der verschiedenen Speicherbereiche sowie verschiedene Garbage-Collection-Statistikdaten ausgegeben werden.

Abbildung 3: jstat Ausgabe

In den Spalten S0U, S1U, EU und OU findet man die aktuelle Auslastung von Survivor Space 0, Survivor Space 1, Eden und Old Generation. Diese Text-Ausgaben kann man sinnvollerweise in eine Datei umlenken, um sie anschließend gesondert zu analysieren. Ähnliche Informationen, aber in interaktiver graphischer Form, erhält man mit visualgc (siehe Abbildung 4). visualgc gibt es als Stand-Alone-Tool und als Plugin in visualvm (siehe / VISV /).
 

Abbildung 4: visualgc Ausgabe

Die visualgc-Ausgabe besteht aus drei Fenstern. Das Fenster links oben zeigt die aktuelle Auslastung von Perm, Old Generation, Eden und den beiden Survivor Spaces. Das Fenster rechts oben zeigt die Größen der verschiedenen Speicherbereiche entlang einer Zeitachse. Im Beispiel (Markierung: weißer Kreis) kann man sehr schön sehen, wie Minor Collections die Objekte zwischen den Survivor Spaces hin- und herkopieren. Dabei reicht die Größe der Survivor Spaces nicht aus, um alle überlebenden Objekte aus der Young Generation aufzunehmen. Die Survivor Spaces laufen über und deshalb steigt zusätzlich die Auslastung der Old Generation. Das geht so lange, bis die Old Generation weitere Objekte auch nicht mehr aufnehmen kann und es zu einer Major Collection kommt. Das dritte und letzte Fenster unten zeigt die so genannte Tenuring Distribution.  Hier kann man sehen, wie oft die Objekte zwischen den Survivor Spaces hin- und herkopiert wurden. Das Fenster besteht aus 32 Rechtecken mit Namen 0 bis 31, weil in unserem Beispiel der Tenuring Threshold 31 ist.  Der Tenuring Threshold ist die maximale Anzahl von Kopiervorgängen zwischen Survivor Spaces, bevor die überlebenden Objekte in die Old Generation kopiert werden.  Ein Objekt, das aktuell 18-mal zwischen den Survivor Spaces kopiert wurde, wird im Rechteck 18 angezeigt. Ein Objekt, das alle Kopiervorgänge überlebt und am Ende in die Old Generation kopiert wird, wandert von links nach rechts durch alle 32 Rechtecke. In unserem Beispiel sieht man, dass es nur einige wenige Objekte gibt, die ein, zwei und drei Kopiervorgänge überlebt haben; alle anderen Rechtecke sind leer.

Natürlich ist das Garbage-Collection-Tuning auch mit Hilfe der Tools ein iterativer Prozess. Man wird nach jedem Ändern der Garbage-Collection-Parameter die Generationenauslastung mit Hilfe der Tools neu überprüfen.  Das geschieht dann insbesondere mit Blick auf das durchgeführte Tuning.

Ein weiterer Aspekt der Objekt-Population, den wir noch kurz diskutieren wollen, ist seine Variation über die Laufzeit der Applikation. Bisher haben wir so getan, als wäre das Speicherverhalten gleichbleibend über die Zeit. Das ist es natürlich nicht. Je nach Branche und Applikation gibt es „Monday Morning Scenarios“, „Rush Hour Peak Times“ oder sonstwie benannte Lastspitzen-Zeiten. Im Allgemeinen wird man das Garbage-Collection-Tuning auf diese Lastspitzen ausrichten. Die Idee dabei ist: wenn es für die Spitzen reicht, wird es auch sonst passen.  Zu diesem Zweck würde man zum Beispiel gleich von Anfang an mit der Option -Xms den initialen Speicher auf einen Wert gesetzt wird, der in etwa dem späteren Maximalbedarf entspricht.  Damit erspart man der JVM, dass sie vom Start bis zur ersten vollen Lastspitze immer wieder neuen Speicher vom System anfordern muss, was nur unnötig Performance kostet.  Bei einem solchen Tuning beansprucht die Applikation zwar die Gesamtmenge an Speicher früher als ohne das Tuning, aber der Speicher muss ja für die Lastspitzen ohnehin später vorhanden sein.  Die beschriebenen Tuning-Überlegungen passen bestimmt für quantitative Variation (mehr Last / weniger Last), aber sie werden nicht immer bei qualitativen Variationen gelten. Was ist damit gemeint?

Nehmen wir an, an einer Server Applikation arbeiten werktags über ein Web-Frontend 5000 Benutzer und in der Nacht von Samstag auf Sonntag laufen Batches für Cleanup und zur Datensicherung. Das durch die Benutzeraktionen erzeugte Speicherverhalten werktags hat vermutlich eine ganz andere Charakteristik als das Speicherverhalten während der einmal wöchentlichen Batchläufe. In der Praxis wird man in einem solchen Fall die Garbage Collection für die interaktiven Lastspitzen tunen und hoffen, dass der Batchbetrieb am Wochenende damit auch läuft. Sollte es wider Erwarten mit dem Batchbetrieb doch Probleme geben, wird man probieren müssen, ob man ein Tuning für beide Szenarien hinbekommt. Eine Alternative ist sonst, die Server-Applikation in der Woche mit einem Tuning für den interaktiven Betrieb laufen zu lassen, und am Wochenende ein Tuning für den Batchbetrieb zu verwenden, oder aber den Batchbetrieb als eine separate Applikation zu fahren. Eine generelle Empfehlung lässt sich an dieser Stelle nicht geben, denn die Lösung hängt von vielen anderen Faktoren (wie zum Beispiel Verfügbarkeitsanforderungen) ab. Wichtig ist, dass man beim Garbage-Collection-Tuning ein halbwegs zutreffendes Bild von der Objekt-Population der Applikation hat. Dabei wird man die typischen Lastspitzen im Fokus haben.

Anpassen der Collectoren

Wie bereits kurz angedeutet, bedeutet Garbage-Collection-Tuning nun, den Garbage Collector (oder besser gesagt die Garbage Collectoren, nämlich den auf der Young und den auf der Old Generation) an die konkrete Objekt-Population der Applikation anzupassen. Dabei  verfolgt man eines der beiden Tuningziele:
  • hoher Durchsatz oder
  • konstant kurze Pausen
Sieht man mal von der reinen Auswahl des Old Generation Garbage Collectors (die wir oben beschrieben haben) ab, so erfolgt das Tuning in den meisten Fällen durch Anpassung des Young Generation Garbage Collectors. Die Gründe dafür sind vielfältig. Ein wesentlicher Grund ist, dass die Young Generation für sich genommen schon aus zwei Speicherbereichen besteht, nämlichen Eden und den Survivor Spaces, die eng miteinander verzahnt sind. Dadurch ergeben sich einfach mehr Einstellmöglichkeiten. Ein anderer Grund, der vor allem beim Tuningziel „hoher Durchsatz“ eine Rolle spielt, ist die deutlich höhere Frequenz der Collections auf der Young Generation im Vergleich zur Häufigkeit der Collections auf der Old Generation. Schafft man es, die Zeit für die Young Generation Collections zu verkürzen, so ist dies deutlich effektiver als die gleiche Verkürzung bei den Old Generation Collections.

Wir können in diesem Artikel nicht alle denkbaren Tuningtipps und –tricks diskutieren, da viele sehr situationsspezifisch sind. Stattdessen sehen wir uns die Grundtechniken für die beiden Tuningziele „hoher Durchsatz“ und „konstant kurze Pause“ an. Davon ausgehend kann man dann im konkreten Fall sein eigenes Tuning starten.

Hoher Durchsatz

Fangen wir mit dem Tuningziel „hoher Durchsatz“ an.  Das Ziel ist, dass die JVM möglichst wenig Zeit mit Garbage Collection zubringt, um möglichst viel Zeit für die eigentliche Applikation zu haben.  Möglichst wenig Zeit mit Garbage Collection zubringen bedeutet bei einem Generational-Garbage-Collection-Ansatz, wie ihn die Sun JVM verfolgt, dass möglichst viele Objekte in der Young Generation (besser sogar gleich noch in Eden) sterben.  Warum ist das so?  Wenn die Objekte Eden überleben, entstehen weitere Garbage-Collector-Aufwände: das erstmalige Kopieren in einen Survivor Space, das mehrmalige Umkopieren von einem Survivor Space in den anderen, das Kopieren in die Old Generation und die finale Garbage Collection in der Old Generation.  Wenn die Objekte in Eden sterben, wird dies alles nicht benötigt.

Jetzt kann man natürlich nicht das Speicherverhalten der Applikation ändern, um möglichst viele Objekte in Eden sterben zu lassen.  Aber man kann Eden möglichst groß machen. Zusätzlich kann man versuchen, die Promotion in die Old Generation dadurch zu verhindern, dass man die Survivor Spaces vergrößert und/oder den Tenuring Threshold erhöht. Damit spart man die Garbage-Collection-Aufwände in der Old Generation.

Nach einem solchen Tuning (Eden vergrößert, Survivor Spaces vergrößert) ist es sinnvoll zu prüfen, ob man dem Tuningziel näher gekommen ist und der Durchsatz sich verbessert hat.  Dazu schaut man sich den Trace-Output der Garbage Collection an.  An dieser Stelle macht es auch Sinn, sich noch mal mit den oben erwähnten Tools die Generationenauslastung anzusehen.  Hat die Vergrößerung von Eden und den Survivor Spaces dazu geführt, so dass nun mehr Objekte „früher“ sterben?  Mit „früher“ ist gemeint: sterben sie nun schon in Eden statt erst in den Survivor Spaces bzw. schon in den Survivor Spaces statt erst in der Old Generation.

Im Prinzip kann man Eden beliebig vergrößern.  Beschränkungen ergeben sich aber aus dem physikalischen Speicher und der Anzahl und Größe anderer Prozesse, die gleichzeitig auf dem Rechner laufen sollen. Für die Survivor Spaces stimmt das nicht uneingeschränkt. Beim Vergrößern der Survivor Spaces gilt anders als bei Eden nicht einfach: größer ist besser.  Bei einer Applikation mit vielen sehr langlebigen Objekten wird man auch durch ein Vergrößern der Survivor Spaces nicht verhindern können, dass diese Objekte erst in der Old Generation sterben. Stattdessen steigt der Aufwand für das Durchschleusen dieser langlebigen Objekte durch die Survivor Spaces, denn größere Survivor Spaces laufen später über und verzögern dadurch die Promotion der langlebigen Objekte.  Der zusätzliche Aufwand für das Durchschleusen kostet natürlich Durchsatz.  Beim Vergrößern der Survivor Spaces gibt es also zwei gegensätzliche Effekte für den Durchsatz.  Objekte mit mittellanger Lebenszeit sterben nun in den Survivor Spaces statt in der Old Generation.  Dies ist der positive Effekt.  Objekte mit sehr langer Laufzeit werden aber länger als bisher zwischen den Survivor Spaces hin- und herkopiert.  Das ist der negative Effekt.  Welcher der beiden Effekte überwiegt, hängt von der Objekt-Population der Applikation sowie der Größe von Survivor Spaces und dem Tenuring Threshold ab.  Im konkreten Fall helfen nur Vergleichstests, bei denen man den Durchsatz misst und sich die Generationenauslastung ansieht, um damit Schlüsse auf das genaue Verhalten ziehen zu können.

Konstant kurze Pausen

Kommen wir nun zum Tuningziel „konstant kurze Pausen“.  Wie zu Anfang dieses Artikels bereits diskutiert ist der Concurrent-Mark-And-Sweep (CMS) Garbage Collector die erste Wahl für den Garbage Collector auf der Old Generation, um lange Pausen für die Old Generation Garbage Collection zu vermeiden.  Leider kommt der CMS Garbage Collector mit gewissen Limitationen. Wir haben uns diese Limitationen bereits ausführlich in einem vorangegangenen Artikel (/ GC4 /) zum CMS angesehen.  Deshalb hier nur eine kurze Zusammenfassung:  Da der CMS keine Kompaktierung auf der Old Generation durchführt, wird der Old Generation Speicherplatz mit fortschreitendem Alter immer fragmentierter.  Im worst case gibt es in der Old Generation keinen ausreichend große Speicherbereich mehr, um überlebende Objekte aus der Young Generation aufzunehmen.  Dann kommt es zu einer Full Garbage Collection (mit Kompaktierung), die sehr lange dauert. Das ist eine Situation, die man eigentlich vermeiden wollte.   Deshalb wurde der CMS ja ausgewählt.

Die Fragmentierung der Old Generation und der Rückfall in die serielle Full Garbage Collection ist bei Benutzung des CMS das zentrale Problem.  Eine einfache Tuningstrategie zur Vermeidung des Problems ist das Vergrößern der Old Generation.  Sie führt dazu, dass sich der Zeitpunkt nach hinten verschiebt, zu dem eine so extreme Fragmentierung auftritt, dass Objekte aus der Young Generation nicht mehr problemlos in der Old Generation untergebracht werden können. Dieser Ansatz mag hilfreich sein in Hinblick auf das Tuningziel „konstant kurze Pausen“, aber eine vollständige Lösung des Problems ist es nicht; man hat das Problem ja nur verschoben.  Das gilt insbesondere für lang laufende Serveranwendungen.  Manche Anwendungen haben jedoch unkritische Zeiträume, in denen eine kompaktierende und damit länger laufende Full Garbage Collection nicht stört, zum Beispiel nachts. In solchen Fällen kann man versuchen, durch Vergrößerung der Old Generation die extreme Fragmentierung solange hinauszuzögern, dass die Garbage-Collection-Pause nachts ist.  Dazu ist es sinnvoll, die kompaktierende Full Garbage Collection explizit auszulösen.

Der bessere Ansatz ist unter Umständen das Vergrößern der Young Generation. Die Idee dahinter ist, dass nur Objekte in die Old Generation kommen, die bis zum Ende der Applikation leben.  Das heißt, alle Objekte, die während der Laufzeit der Applikation sterben, sollen dies in der Young Generation tun. So lässt sich ganz sicher die Fragmentierung Old Generation vermeiden, denn dort sterben dann keine Objekte mehr. Da wir nicht verlangen können, dass alle Objekte schon in Eden sterben, nehmen wird die Survivor Spaces zur Hilfe.  Das Tuning besteht darin, den Tenuring Threshold zu erhöhen, damit die Objekte lange zwischen den Survivor Spaces hin- und herkopiert werden. Dabei sterben sie hoffentlich, so dass nur die wirklich extrem langlebigen Objekte in die Old Generation kommen.  Damit die Survivor Spaces nicht in die Old Generation überlaufen, wird man auch sie im Rahmen des Tunings vergrößern müssen.  Der Nachteil bei diesem Tuning ist, dass es mit großer Wahrscheinlichkeit den Durchsatz reduziert.  Wie weit das zu tolerieren ist, hängt jeweils vom Einzelfall ab.

Wie bereits erwähnt, liegt diesem Tuningansatz das Prinzip zu Grunde, dass keine Objekte in der Old Generation sterben.  Ob sich dies in der Praxis umsetzen lässt, hängt von der Objekt-Population der Applikation ab.  Im Allgemeinen wird man leider nicht erwarten können, dass in der Old Generation überhaupt keine Objekte mehr sterben.  Gerade bei Serverapplikationen im Dauerbetrieb - vierundzwanzig Stunden am Tag / sieben Tage die Woche - wird man wohl gelegentlich in die Situation extremer Fragmentierung kommen.  Hier hilft dann vielleicht die bereits oben erwähnte explizit eingeleitete, kompaktierende Full Garbage Collection zu einem unkritischen Zeitpunkt.

Neben dem Tuning der Speichergrößen, das wir uns bisher zu diesem Thema angesehen haben, kann man aber auch die Funktion des CMS selbst tunen.  Da der CMS konkurrierend zur Applikation läuft, geht es darum, ihn so abzustimmen, dass die beiden folgenden Extremsituationen vermieden werden:

  • Der CMS läuft zu viel und „stiehlt“ der Applikation die CPU.
  • Der CMS läuft zu wenig und es kommt zur unerwünschten Full Garbage Collection, weil der CMS nicht schnell genug ausreichend viel Speicher freigibt.
Für das Tuning gibt es einen Parameter: die relative Speicherauslastung ( occupancy fraction ) der Old Generation, bei der der CMS mit einem Garbage-Collection-Zyklus beginnt.  Der Defaultwert ist etwa 68 Prozent.  Setzt man den Wert mit -XX:CMSInitiatingOccupancyFraction=<percent> herunter, so startet der CMS früher und läuft somit mehr.  Der per Option angegebene Schwellenwert wird vom CMS jedoch auf Basis von Statistikdaten immer wieder angepasst, das heißt, der Schwellenwert ist nur der Anfangswert und wird vom CMS später geändert.  Für diese dynamischen Anpassungen werden Daten über den freien Speicher in der Old Generation und die Menge und Größe der Objekte herangezogen, die bei vorangegangenen Collections per Promotion in die Old Generation verschoben wurden.  Um also Änderungen am explizit mitgegebenen Schwellenwerts zu vermeiden, muss zusätzlich die Option -XX:+UseCMSInitiatingOccupancyOnly benutzt werden.

Da der CMS den Schwellenwert auf Basis der vorhergehenden Collections „lernt“, ist das explizite Setzen des Wertes typischerweise bei solchen Applikationen nötig, deren Objektpromotion in die Old Generation stark schwankt.

Selbstanpassung der Collectoren

Alle in diesem Beitrag beschriebenen Feintuning-Strategien beruhen darauf, dass man versucht, ein Bild von der Objekt-Population der Anwendung zu gewinnen und dann die ausgewählten Collectoren entsprechend einzustellen.  In gewissem Umfang können die Garbage Collectoren dies auch selber tun, indem sie Daten über vergangene Collections sammeln und daraus Heuristiken für zukünftige Collections ableiten – ähnlich wie wir es eben für die Occupancy Fraction des CMS-Collectors beschrieben haben.  Diese dynamische Selbstadaption wird als Garbage Collection Ergonomics bezeichnet; sie betrifft die Größen von Young und Old Generation.

Man stellt die Selbstadaption mit der Option -XX:+UseAdaptiveSizePolicy ein und kann zusätzlich noch spezifische Ziele vorgeben: ein Pausenziel mit -XX:MaxGCPauseMillis=<millis> und ein Durchsatzziel mit -XX:GCTimeRatio=<ratio> .  Dabei bedeutet -XX:GCTimeRatio=nnn , dass nicht mehr als 1 / (1 + nnn) der Ablaufzeit für Garbage Collection verwendet werden darf.  Wenn man die Ziele nicht angibt, dann ist kein Pausenziel vorhanden und das Durchsatzziel ist 99%.  Mit der Option –XX:+PrintAdaptiveSizePolicy kann man Informationen über die Anpassungen im Trace-Output bekommen.

Wenn die Selbstadaption eingeschaltet ist, dann versucht der Garbage Collector, die Generationengrößen so anzupassen, dass die Ziele möglichst erreicht werden.  Dass er sie erreicht, ist jedoch nicht garantiert, denn es hängt vom Speicherverhalten der Applikation ab, ob sie überhaupt erreichbar sind. Wenn die vorgegebenen Ziele realistisch sind, dann sind die eingebauten Heuristiken passend für solche Anwendungen, deren Speicherverhalten stetig ist und wenig schwankt.  Bei Anwendungen mit stark schwankendem Speicherverbrauch können die Heuristiken aber auch kontraproduktiv sein. Genauso wie beim manuellen Fein-Tuning muss man sich in jedem Fall die Ergebnisse der Selbstadaption ansehen und gegebenenfalls mit den in diesem Beitrag beschriebenen Maßnahmen manuell ergreifen.  Weitere Details zu Garbage Collection Ergonomics finden sich unter / ERG /.

Zusammenfassung

In diesem Beitrag haben wir uns das Vorgehen beim Garbage-Collection-Tuning angesehen.  Wenn man sich klar über das Tuningziel (entweder „hoher Durchsatz“ oder „konstant kurze Pausen“) ist, sind die beiden wesentlichen Schritte beim Tuning:
  • Auswahl der Garbage Collectoren für Young und Old Generation entsprechend dem Tuningziel, und danach
  • Anpassen der ausgewählten Collectoren an die Objekt-Population der Applikation unter Berücksichtigung des Tuningziels.
Wir haben diskutiert, wie die Objekt-Population der Applikation sich auf die Auslastung der verschiedenen Garbage-Collector-Generationen abbildet und welche Tools man verwenden kann, um sich die konkrete Generationenauslastung einer Applikation anzusehen.
Danach haben wir uns das grundsätzliche Vorgehen beim Anpassen der Garbage Collectoren zur Erreichung der Tuningziele angesehen. Die Auswahl der Garbage Collectoren hängt von der Zahl der Cores und dem Ziel ab: CMS für kurze Pausen und serielle oder parallele Young/Old Collection ansonsten. Im Rahme des Feintunings vergrößert man Eden, die Survivor Spaces und den Tenuring Threshold für das Ziel „hoher Durchsatz“.  Für „konsistent kurze Pausen“ verfährt man ähnlich und verlagert die Full Garbage Collection auf unkritische Zeiträume, in denen lange Pausen nicht stören.

Nun haben wir bei weitem nicht alle denkbaren und möglichen Maßnahmen für ein Feintuning besprochen.  Man findet verstreut über diverse Dokumente zahlreiche weitere Details über die Einstellmöglichkeiten der Collectoren. Leider gibt es kein Dokument, in dem alles übersichtlich zusammengefasst ist.  Das liegt teilweise auch daran, dass sich die JVM-Optionen von Release zu Release ändern, neue hinzukommen, andere wegfallen.  Wer sich für das Detail-Tuning einzelner Collectoren interessiert findet in der Literaturliste einige Hinweise auf nützliche Webseiten (siehe / SDN / und / MAS /).  Zusammen mit den Erläuterungen, die wir zur Funktionsweise der einzelnen Collectoren in / GC2 / bis / GC4 / gegeben haben, kann man den Kontext und die Effekte der Detail-Einstellungen einigermaßen verstehen.  Wir haben uns in diesem Beitrag auf die wichtigsten und wesentlichen Aspekte des Garbage-Collection-Tunings beschränkt und beenden damit die Betrachtungen zum Tuning.  Im nächsten Beitrag werden wir uns dem neuen Garbage First (G1) Collector zuwenden, der in Java 7 der Default werden soll.

Literaturverweise

/JSTT/ jvmstat 3.0 – jvmstat Technology
Sun Developer Network (SDN)
URL: http://java.sun.com/performance/jvmstat/
/VISV/  visualvm – Java VisualVM
Sun Developer Network (SDN)
URL: http://java.sun.com/javase/6/docs/technotes/guides/visualvm/index.html
/SDN/  Java HotSpot Garbage Collection
Sun Developer Network (SDN)
URL:  http://java.sun.com/javase/technologies/hotspot/gc/index.jsp
/MAS/ Jon Masamitsu's Weblog on Garbage Collection
URL: http://blogs.sun.com/jonthecollector/category/Java
/FLGS/ HotSpot JVM Flags
URL: http://www.tagtraum.com/gcviewer-vmflags.html
URL: http://java.sun.com/javase/technologies/hotspot/vmoptions.js p
URL: http://java.sun.com/docs/hotspot/gc1.4.2/faq.html
URL: http://blogs.sun.com/roller/resources/watt/jvm-options-list.html
URL: http://www.md.pp.ru/~eu/jdk6options.html
/ERG/ Garbage Collection Ergonomics
Sun Developer Network (SDN)
URL: http://java.sun.com/j2se/1.5.0/docs/guide/vm/gc-ergonomics.html

Die gesamte Serie über Garbage Collection:

/GC1/ Generational Garbage Collection
Klaus Kreft & Angelika Langer, Java Magazin, February 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/49.GC.GenerationalGC/49.GC.GenerationalGC.html
/GC2/ Young Generation Garbage Collection
Klaus Kreft & Angelika Langer, Java Magazin, April 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/50.GCYoungGenGC/50.GC.YoungGenGC.html
/GC3/ Old Generation Garbage Collection – Mark-and-Compact
Klaus Kreft & Angelika Langer, Java Magazin, June 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/51.GC.OldGen.MarkCompact/51.GC.OldGen.MarkCompact.html
/GC4/ Old Generation Garbage Collection – Concurrent-Mark-Sweep (CMS)
Klaus Kreft & Angelika Langer, Java Magazin, August 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/52.GC.OldGen.CMS/52.GC.OldGen.CMS.html
/GC5/ Garbage Collection Tuning – Die Ziele
Klaus Kreft & Angelika Langer, Java Magazin, Oktober 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/53.GC.Tuning.Goals/53.GC.Tuning.Goals.html
/GC6/ Garbage Collection Tuning – Die Details
Klaus Kreft & Angelika Langer, Java Magazin, Dezember 2010
URL: http://www.angelikalanger.com/Articles/EffectiveJava/54.GC.Tuning.Details/54.GC.Tuning.Details.html
/GC7/ Der Garbage-First Garbage Collector (G1) - Übersicht über die Funktionalität
Klaus Kreft & Angelika Langer, Java Magazin, Februar 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/55.GC.G1.Overview/55.GC.G1.Overview.html
/GC8/ Der Garbage-First Garbage Collector (G1) - Details und Tuning
Klaus Kreft & Angelika Langer, Java Magazin, April 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/56.GC.G1.Details/56.GC.G1.Details.html
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
 
High-Performance Java - programming, monitoring, profiling, and tuning techniques
4 day seminar ( open enrollment and on-site)
Garbage Collection Tuning - profiling and tuning of memory related performance issues
1 day seminar ( open enrollment and on-site)
 
  © Copyright 1995-2012 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/54.GC.Tuning.Details/54.GC.Tuning.Details.html  last update: 1 Mar 2012