Nützliche JVM Flags – Teil 3

Keine Kommentare

Es passiert nur selten, dass man sich darüber freut etwas Falsches geschrieben zu haben. Genau so ging es mir aber neulich! Im zweiten Teil unserer Serie hatte ich geschrieben, dass man die XX-Flags bei der HotSpot-JVM nicht auflisten kann. Diese Aussage war zwar lange Zeit richtig, seit Kurzem ist sie aber falsch.

Als ich letzte Woche die neueste Ausgabe des exzellenten Java Performance Tuning Newsletters öffnete, fiel mein Blick sofort auf zwei Artikel zum Thema JVM Flags. Dort steht doch tatsächlich geschrieben, dass man neuerdings auch bei der HotSpot-JVM alle XX-Flags auf der Kommandozeile ausgeben lassen kann. Und nicht nur das: Nebenbei erhält man sogar Informationen über sämtliche Standardwerte der Flags sowie die tatsächlich gesetzten Werte nach JVM-Start. Fantastisch. All I ever wanted! Grund genug, uns das mal näher anzuschauen.

-XX:+PrintFlagsFinal und -XX:+PrintFlagsInitial

Seit einem der jüngsten Updates von Java 6 bringt die HotSpot-JVM die beiden Flags -XX:+PrintFlagsFinal und -XX:+PrintFlagsInitial mit. Wann exakt die beiden Flags erschienen sind, habe ich nicht genau nachgeprüft, aber ich kann es auf Java 6 Update 20 oder 21 eingrenzen. Getestet habe ich die Flags mit Java 6 Update 23.

Werfen wir direkt einen Blick auf die Ausgaben von -XX:+PrintFlagsFinal. Ein Aufruf der Client-VM ohne weitere Parameter liefert eine alphabetisch sortierte Liste von immerhin 590 XX-Flags (hier gekürzt dargestellt):

$ java -XX:+PrintFlagsFinal Benchmark
[Global flags]
uintx AdaptivePermSizeWeight               = 20               {product}
uintx AdaptiveSizeDecrementScaleFactor     = 4                {product}
uintx AdaptiveSizeMajorGCDecayTimeScale    = 10               {product}
uintx AdaptiveSizePausePolicy              = 0                {product}
[...]
uintx YoungGenerationSizeSupplementDecay   = 8                {product}
uintx YoungPLABSize                        = 4096             {product}
 bool ZeroTLAB                             = false            {product}
 intx hashCode                             = 0                {product}

Jede Zeile beschreibt ein Flag und besteht aus fünf Spalten. Spalte 1 zeigt den Datentyp der Option, Spalte 2 ihren Namen, Spalte 4 ihren Wert und Spalte 5 ihre Kategorie. In Spalte 3 steht entweder ein „=“ oder ein „:=“. Ein „=“ bedeutet, dass der Wert in Spalte 4 der Default-Wert für die Option ist. Ein „:=“ hingegen zeigt uns, dass die Option gezielt (vom Benutzer oder von der JVM in Abhängigkeit der Arbeitsumgebung) auf den Wert in Spalte 4 gesetzt wurde.

Zu erwähnen ist, dass ich die Klasse „Benchmark“ beim Aufruf von java hier nur verwende, weil ich sie gerade zur Hand habe (und sie aus den vorangegangenen Teilen dieser Serie schon bekannt sein dürfte). Man kann die Ausgaben aber genauso erhalten, wenn man java mit dem Parameter -version und ohne Main-Klasse startet.

Sehen wir einmal nach, wie viele Optionen die Server-VM mitbringt. Um weitere Flags freizuschalten, setzen wir zusätzlich die beiden Flags -XX:+UnlockExperimentalVMOptions und -XX:+UnlockDiagnosticVMOptions:

$ java -server -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal Benchmark

Als Ergebnis erhalten wir eine Liste von 724 Flags. Respekt! Betrachten wir nun die Liste der Flags, gezielt gesetzt wurden:

$ java -server -XX:+PrintFlagsFinal Benchmark | grep ":"
uintx InitialHeapSize                     := 57505088         {product}
uintx MaxHeapSize                         := 920649728        {product}
uintx ParallelGCThreads                   := 4                {product}
 bool PrintFlagsFinal                     := true             {product}
 bool UseParallelGC                       := true             {product}

Eines der Flags, nämlich -XX:+PrintFlagsFinal, haben wir selbst gesetzt. Die anderen Flags hat die Server-VM eigenständig in Abhängigkeit der verwendeten Hardware gesetzt, um den Heap angemessen zu dimensionieren und einen parallelen Garbage Collector zu aktivieren.

Wer an den Default-Werten der XX-Flags interessiert ist und keine gezielt gesetzten Werte sehen möchte, kann alternativ das Flag -XX:+PrintFlagsInitial verwenden. Hier finden sich in den Ausgaben in Spalte 3 nur „=“ und keine „:=“. Allerdings fehlen einige Flags gegenüber der Liste, die man mit -XX:+PrintFlagsFinal erhält, vermutlich weil sie dynamisch erzeugt werden.

Sehr interessant ist es, anhand der Ausgaben das Verhalten von Client- und Server-VM zu vergleichen. Auch kann man prima nachprüfen, welche Flags heimlich die Werte anderer Flags verändern, wenn man sie auf der Kommandozeile setzt. Genau das wird in dem hervorragenden Blog-Eintrag Inspecting HotSpot JVM Options gemacht, den ich hiermit wärmstens zur Lektüre empfehle. In diesem Eintrag wird ebenfalls kurz auf die Bedeutung der Kategorien in Spalte 5 eingegangen.

-XX:+PrintCommandLineFlags

Um das Thema „Ausgabe von XX-Flags“ abzuschließen, erwähne ich noch kurz ein Flag, das schon wesentlich länger verfügbar ist: -XX:+PrintCommandLineFlags. Mit diesem gibt die JVM bei ihrem Start die Namen und Werte aller XX-Flags aus, die gezielt gesetzt wurden. Dabei handelt es sich also um exakt diejenigen Flags, die in der Ausgabe von -XX:+PrintFlagsFinal mit „:=“ gelistet werden. Daher kann -XX:+PrintCommandLineFlags als Abkürzung verwendet werden, falls man nur an diesen Ausgaben interessiert ist. Greifen wir das obige Beispiel wieder auf:

$ java -server -XX:+PrintCommandLineFlags Benchmark
-XX:InitialHeapSize=57505088 -XX:MaxHeapSize=920081408 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseParallelGC

Schreibt man diese Ausgaben in eine Log-Datei, so dokumentiert man damit letzten Endes die Auswirkungen von Flags auf die Performance der Anwendung. Ähnlich wie -showversion (siehe Teil 1 dieser Serie) ist -XX:+PrintCommandLineFlags damit ein Flag, das man bei jedem JVM-Aufruf verwenden sollte. Denn man weiß nie, wann man diese Information noch einmal gebrauchen kann.

Interessant ist, dass – nicht nur bei unserem Beispiel – die maximale Heap-Größe gelistet mit -XX:+PrintCommandLineFlags etwas kleiner ist als der von -XX:+PrintFlagsFinal gelieferte Wert. Wer weiß, warum das so ist?

Wie geht es weiter?

Wären mir nicht die beiden brandneuen Flags begegnet, so hätte heute schon einer der geplanten Bereiche „Heap-Flags“ oder „Garbage-Collection-Flags“ auf dem Programm gestanden. Immerhin haben wir oben schon einen kleinen Teaser bestehend aus einer Handvoll Flags zu diesen Bereichen gesehen. Im nächsten Teil der Serie steigen wir tiefer in diese Thematik ein.

Patrick Peschlow

Dr. Patrick Peschlow ist Entwicklungsleiter bei der CenterDevice GmbH und verantwortlich für die Architektur sowie die technische Umsetzung der Anwendung. Es gibt Leute, die sagen, DevOps sei sein zweiter Vorname.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.