Inhalt

Hintergrund

Das Tuning der Systemleistungen ist ein erstaunlich verzwickter Prozess, der einen methodischen Ansatz und eine klare Vorstellung vom gewünschten Ausgang erfordert. Die standardmäßigen JVM-Tunings versuchen akzeptable Leistungen für den überwiegenden Teil der denkbaren Situationen zu bieten. Abhängig vom Verhalten der Applikationen und ihrer Auslastung können die standardmäßigen Einstellungen jedoch nicht immer zu den angestrebten Ergebnissen führen. Wenn die JVM-Speicherbereinigung nicht wie gewünscht ausgeführt wird, müssen Sie Ihre Applikation bewerten und mit der JVM abstimmen, um ein ansprechendes Maß an Speicherbereinigungs-Parametern zu erhalten.

Bevor Sie beginnen, sollten Sie die folgenden Faktoren berücksichtigen, die für die Festlegung Ihrer Tuning-Parameter entscheidend sind:

  • Die Version der Applikation, die von der Java Virtual Machine (JVM) ausgeführt wird
  • Die Version des Java Development Kits (JDK)
  • Die Server-Hardware
  • Das Betriebssystem
  • Das Belastungsprofil des Systems
  • Den Datenbestand des Systems.

All diese Punkte bestimmen die Umgebung, für die Sie die Ausführung der Speicherbereinigung anpassen wollen. Je genauer Sie Ihre Tuning-Parameter festlegen, desto spezifischer werden Ihre Lösungen und bleiben damit auf umso weniger Umgebungen begrenzt. Das bedeutet, wenn sich eine der Variablen ändern sollte (etwa wenn mehr Nutzer die Berechtigung für Applikations-Anfragen erhalten oder die Applikation/die Hardware ein Upgrade erfährt), dann müssen Sie alle Ihre Leistungs-Tunings noch einmal überprüfen.

Was die Speicherbereinigung nicht leisten kann

Das Tuning der Speicherbereinigungs-Leistung kann Sie nur einen Teil des Weges bringen. Sie werden den Punkt erreichen, an dem Sie keine weiteren Steigerungen mehr durch Veränderungen erzielen können - etwa, wenn Sie an die Leistungsgrenzen der Systemumgebung stoßen. Wenn das passiert und Sie noch immer weit von Ihrem Ziel entfernt sind, sollten Sie andere Änderungen erwägen, die über die Speicherbereinigung hinausgehen, wie die Anschaffung leistungsstärkerer Hardware, OS-Tuning und Applikations-Tuning.
Sie sollten auch beachten, dass Sie mit der Durchführung bestimmter Tunings die Systemleistung unter Umständen verringern können. Es ist daher wichtig, dass Sie Ihre Applikation regelmäßig überwachen und prüfen, ob die Annahmen noch immer zutreffen, auf denen Ihr Tuning basiert.

Überblick über die Speicherbereinigungs-Prozedur

Mit diesem Dokument zeigen wir Ihnen, wie Sie die Speicherbereinigungs-Leistung tunen können. Dazu müssen Sie die folgenden Schritte ausführen:

  1. Leistungsziele auswählen
  2. Die Systemumgebung für das Tuning vorbereiten
  3. Den Oracle HotSpot VM's throughput-Collector verstehen
  4. Mit dem Tuning beginnen!

1. Leistungsziele auswählen

Der erste Schritt zum Tuning der Speicherbereinigungs-Leistung besteht in der Auswahl der gewünschten Ziele. In einem zweiten Schritt müssen Sie den Zielen konkrete Werte zuordnen, um das System auf das Speicherbereinigungs-Tuning vorzubereiten.

In diesem Abschnitt:

  • Leistungsziele für die Speicherbereinigung
    • Latenzzeit
    • Datendurchsatz
    • Speicherbedarf
  • Oracle HotSpot VM Speicherbereinigungs-Prinzipien
    • Speicherbereinigungs-Rückgewinnung reduzieren
    • Maximierung der Speicherbereinigung durch das Speicherprinzip
    • Das "Zwei aus Drei"-Prinzip

Leistungsziele für die Speicherbereinigung

Die Speicherbereinigung mit dem Oracle HotSpot VM kann auf die folgenden drei Ziele reduziert werden:

  • Latenzzeit - Pausen, die von der JVM induziert werden, während die Speicherbereinigung ausgeführt wird
  • Datendurchsatz - Der Prozentsatz an Zeit, die der JVM für die Ausführung der Applikation zur Verfügung steht
  • Speicherbedarf - Die erforderliche Menge an Speicherplatz
Latenzzeit

Die Latenzzeit entspricht einem Teil der Zeit, die Nutzer einer Applikation auf die Antwort ihrer Anfrage warten müssen – wie zum Beispiel dem Besuch des Jira-Dashboards oder der Suche nach Vorgängen. Bezogen auf die Speicherbereinigung bildet die Latenzzeit die Zeitspanne, in der die JVM pausiert und die Applikation nicht ausführen kann. Für die Latenzzeit gibt es zwei wesentliche Messgrößen: die generelle Speicherbereinigungs-Latenzzeit und die maximale Latenzzeit. Anhand der generellen Speicherbereinigungs-Latenzzeit lässt sich erkennen, wie lange die Speicherbereinigungs-Pause normalerweise sein wird. Das Maximum beschreibt hingegen die maximale Pausenzeit, die zu erwarten ist. Die Motivation für dieses Ziel ist meist mit der Leistung bzw. der Reaktionszeit des Clients verbunden. Außerdem sollte bedacht werden, ob die Applikation mit anderen Systemen verbunden ist, sodass Verbindungs-Auszeiten oder Leistungsverzögerungen auch andere Systeme beeinflussen könnten.

Die Latenzzeit wird in Sekunden angegeben. Für das Tuning der Speicherbereinigung sind die beiden Hauptziele die Latenzzeit betreffend:

  • Generelle Pausenzeit und
  • Maximale Pausenzeit.
Datendurchsatz

Der Datendurchsatz entspricht der durchschnittlichen Zeit, die einer Applikation für die Ausführung zur Verfügung steht. Über je mehr Zeit eine Applikation zur Ausführung verfügt, desto mehr Verarbeitungszeit ist für Service-Anfragen verfügbar. Bitte beachten Sie, dass ein hoher Datendurchsatz und eine geringe Latenzzeit nicht unbedingt miteinander verknüpft sein müssen. Ein hoher Datendurchsatz kann von langen unregelmäßigen Pausenzeiten begleitet sein.

Speicherbedarf

Der Speicherbedarf entspricht der Menge an Speicher, die von der JVM für die Ausführung der Applikation verbraucht wird. Diese Größe ist normalerweise wichtig, wenn Ihre Systemumgebung begrenzt ist.

Oracle HotSpot VM Speicherbereinigungs-Prinzipien

Um Ihre Ziele zu erreichen, sollten Sie sich an den folgenden drei Prinzipien orientieren, die Ihnen helfen werden, den HotSpot für die Speicherbereinigung zu tunen:
Speicherbereinigungs-Rückgewinnung reduzieren - Geringere Kosten für reduzierte Speicherbereinigungs-Rückgewinnung
Maximierter Speicher - Je mehr Speicher, desto besser
Zwei aus Drei - Wählen Sie zwei der drei Leistungsziele

Speicherbereinigungs-Rückgewinnung reduzieren

Vorausgesetzt ist eine generalisierte Speicherbereinigung, für die angenommen wird:

  • Die meisten Speicher-Zuordnungen sind kurzlebig.
  • Die meisten alten Objekte beziehen sich ausschließlich auf alte Objekte.
  • Die meisten alten Objekte sind langlebig.
  • Die Effizienz der Speicherbereinigung steht proportional zu den noch aktiven Objekten.
    Für die Mehrzahl der Applikationen gilt, dass Garbage hauptsächlich durch kürzliche kurzlebige Objekt-Zuordnungen entsteht.

Diese Annahmen implizieren, dass es effizienter ist, die Speicherbereinigung zwischen Speicherbereichen für neue und alte Zuordnungen aufzuteilen. Im neuen Bereich (oder der neuen Generation) kann schneller gearbeitet werden, wenn weniger aktive Objekte am Ende einer Sammlung stehen. Gegenteiliges gilt für die alte Generation, in der Sammlungen am Ende meist mehr aktive Objekte ergeben.

Was bedeutet das?

Je weniger kurzlebigen Zuordnungen, die von der alten zur neuen Generation aufsteigen oder beansprucht werden sowie umgekehrt die eher langlebigen Zuordnungen, die von neu zu alt beansprucht werden, desto effizienter verläuft die generelle Speicherbereinigung der JVM. Das führt zu einem höheren Durchsatz.

Maximierter Speicher

„Wenn uns unendlicher Speicher zur Verfügung steht, brauchen wir keine Speicherbereinigung durchführen!“

Je mehr Speicher der JVM zur Verfügung steht, desto niedriger wird die Speicherbereinigungs-Frequenz. Zudem bedeutet das auch, dass die neue Generation wesentlich besser auf die Entstehungsrate der kurzlebigen Objekte abgestimmt werden kann. So reduziert sich die Anzahl der Zuordnungen, die in der alten Generation angeregt werden.

Das "Zwei aus Drei"-Prinzip

Um die Sache zu vereinfachen, empfehlen wir, dass Sie für Ihr Tuning nur zwei der drei Leistungsziele auswählen und eines opfern. Noch einfacher wird es, wenn Sie sich nur auf ein Ziel festlegen. Häufig konkurrieren die Ziele miteinander. Beispielsweise ist es wahrscheinlich, dass die generellen Pausen länger werden, je weniger Speicher Sie bereitstellen. Umgekehrt ist es auch möglich, dass sich die Pausen-Anzahl erhöht und dadurch den Durchsatz verringert, wenn Sie weniger Speicher zur Verfügung stellen und die generelle Pausenzeit reduzieren. Gleiches gilt für die Datenstruktur: Wenn der Speicher für alle Bereiche aller Generationen und Untergenerationen so angepasst sind, dass bessere Latenzzeit und höherer Durchsatz ermöglicht sind, geht das meist zu Kosten des JVM-Speicherbedarfs.

Kurz gesagt: Die Abstimmung der Speicherbereinigung ist ein Balance-Akt. Es wird Ihnen wahrscheinlich nicht möglich sein, alle Ziele nur durch das Tuning der Speicherbereinigung zu erreichen.

2. Die Systemumgebung für das Tuning vorbereiten

Sobald Sie Ihre Ziele ausgewählt haben, müssen Sie Ihre Systemumgebung auf das Tuning der Speicherbereinigung vorbereiten. Mit diesem Schritt ordnen Sie Ihren Zielen feste Werte zu. Gemeinsam bilden die Ziele und Werte die Systemvoraussetzungen der Umgebung, für die Sie das Tuning durchführen möchten.

In diesem Abschnitt:

  • Arbeitsauslastung der Applikation
  • Protokollierung der Speicherbereinigung aktivieren
  • Daten im Abfrage-Fenster bestimmen
  • Speicherbedarf festlegen
  • Faustregeln für Generationengrößen
  • Systemvoraussetzungen ermitteln
Arbeitsauslastung der Applikation

Ehe Sie die Speicherbereinigungs-Leistung der JVM bei der Ausführung einer bestimmten Applikation messen können, müssen Sie in der Lage sein, mit der Applikation Arbeiten auszuführen und dabei einen stabilen Zustand zu erreichen. Hierfür versehen Sie die Applikation per load mit Arbeiten. Wie Sie die Applikation mit Arbeiten auslasten, liegt nicht in der Zuständigkeit dieser Anleitung. Wir möchten Ihnen daher empfehlen, die Arbeitslast an den stabilen Zustand anpassen, den Sie für die Speicherbereinigung erreichen wollen. Beispielsweise sollte die Arbeitslast das Nutzungsverhalten und die Auslastung der Applikation in der Systemumgebung widerspiegeln.

Wenn sie schrittweise Veränderungen an den JVM-Tuning-Parametern vornehmen müssen, raten wir dazu, dass Sie eine Umgebung schaffen, die der Systemumgebung so sehr ähnelt wie möglich (zum Beispiel ähnliche Hardware, OS Version, Arbeitsauslastung). So können Sie Fehler im Tuning vermeiden und Betriebsstörungen für die Nutzer umgehen. Da der Prozess unter Umständen mehrfach wiederholt werden muss, sollten Sie die Störungen für die Nutzer bedenken, falls das System bereits in Produktion ist.

Protokollierung der Speicherbereinigung aktivieren

Nur für Oracle VM
Bitte beachten Sie, dass die nachfolgenden JVM-Parameter ausschließlich für die Oracle 1.6 JVM gelten. Andere Implementierungen müssen diese spezifischen Parameter nicht unbedingt teilen.
Für mehr Informationen vergleichen Sie bitte die JVM-Dokumentation.

Damit Sie die Speicherbereinigungs-Leistung der JVM messen können, müssen Sie die Protokollierung der Speicherbereinigung aktivieren. Dazu nutzen Sie die Befehlszeilen, die während der Inbetriebnahme der JVM angezeigt werden. Zum Beispiel:

java \--XX:+printGC \--XX:+PrintGCDetails \--XX:PrintGCTimeStamps \--Xloggc:<file> ...

Wir empfehlen Ihnen dringend eine separate Protokolldatei für die Speicherbereinigung, um die Speicherbereinigungs-Diagnostik von den Protokolldateien der Applikation zu trennen. Falls Sie (im Gegensatz zu Offset seit Starten der VM) Zeitstempel mit Datum und Uhrzeit bevorzugen, sollten Sie stattdessen --XX:+PrintGCDateStamps verwenden.

Daten im Abfrage-Fenster bestimmen

Wenn Sie die Abfrage-Fenster der Speicherbereinigungs-Protokolldateien überprüfen, ist es wichtig, dass diese Zeitabschnitte repräsentieren, in denen die Applikation mit der Zielauslastung arbeitete, für die Sie das Tuning durchführen. Hierbei ist auch zu beachten, dass es eine gewisse Zeit dauern kann, bis die Applikation und die JVM diesen stabilen Zustand erreichen. Beispielsweise kann das Urladen ein paar Minuten in Anspruch nehmen, ehe die Applikation Service-Abfragen durchführen kann. Oder es vergeht eine halbe Stunde bis alle langlebigen Chases vorbereitet sind (was für den Speicherplatzbedarf mit einkalkuliert werden sollte).

Sie werden Ihre eigene Urteilsfähigkeit schulen müssen, um zu beurteilen, welche Intervalle einen stabilen Zustand für die Applikation in Ihrer Systemumgebung repräsentieren.

Speicherbedarf festlegen

Für das Tuning der JVM Generationsgrößen benötigen Sie eine klare Vorstellung von der stabilen Zustandsgröße der aktiven Daten. Sie können die Größenordnungen auf zwei Arten bestimmen:

  • Raten
  • aus den Speicherbereinigungs-Protokolldateien ableiten

Das setzt jedoch voraus, dass die Applikation in der VM läuft und den stabilen Zustand erreichen kann. Wenn ihnen das nicht möglich ist, probieren Sie folgendes:

  • Räumen Sie der JVM so viel Datenstrukturgröße zu wie maximal möglich ist, ohne dass der Speicher auf die Platte übergreifen muss.
  • Nutzen Sie den Durchsatz-Collector (-XX:+UseParallelOldGC) mit voreingestellten Parametern.

Lassen Sie die JVM laufen und überprüfen Sie, dass keine Speicher-bezogenen Fehler wie java.lang.OutOFMemoryError auftreten. Falls es Fehler geben sollte und falls Sie so viel Speicher wie nur möglich für die Datenstruktur zur Verfügung gestellt haben, müssen Sie die Parameter extern ändern und etwa die Menge der Systemspeicher erhöhen.

Sobald Ihre Applikation in einem stabilen Zustand läuft, müssen Sie den Speicherbedarf der durchschnittlichen alten und der gegenwärtigen Generations-Auslastung mit voller Speicherdatenbereinigung abschätzen. hier ist ein Beispiel für eine volle Speicherbereinigungs-Protokolldateien mit der Beanspruchung der alten und der gegenwärtigen Generation nach der Speicherbereinigung, markiert durch ^ und entsprechend +:

773.192: [Full GC [PSYoungGen: 299756K->0K(6552704K)] [ParOldGen: 11486930K->6622879K(11657024K)] 11786686K->6622879K(18209728K) [PSPermGen: 161761K->160673K(247808K)], 43.4385540 secs] [Times: user=181.10 sys=1.68, real=43.44 secs] ^^^^^^^ ++++++

Bestimmen Sie den Durchschnitt diese Felder für die Perioden stabiler Zustände. Dies entspricht dem durchschnittlichen Speicherbedarf der alten und der gegenwärtigen Generation.

Beispielsweise nutzen die folgenden vollständingen Speicherbereinigungs-Befehle ein einziges Zeitfenste, mit einem Ausgleeich von 300 Sekunden über die Dauer von 3600 Sekunden:

287.223: [Full GC [PSYoungGen: 682071K->0K(9521152K)] [ParOldGen: 1709104K->862326K(1929728K)] 2391175K->862326K(11450880K) [PSPermGen: 164815K->164809K(262144K)], 1.3367950 secs] [Times: user=7.12 sys=0.07, real=1.34 secs] 1125.789: [Full GC [PSYoungGen: 1069485K->0K(10115264K)] [ParOldGen: 2026464K->1577765K(2659264K)] 3095949K->1577765K(12774528K) [PSPermGen: 165949K->165940K(262144K)], 2.9698460 secs] [Times: user=14.42 sys=0.04, real=2.97 secs] 2204.512: [Full GC [PSYoungGen: 1026559K->0K(10101568K)] [ParOldGen: 2710240K->1757747K(2980288K)] 3736799K->1757747K(13081856K) [PSPermGen: 166249K->166242K(262144K)], 3.2056200 secs] [Times: user=15.50 sys=0.02, real=3.21 secs] 2265.623: [Full GC [PSYoungGen: 410046K->0K(10115584K)] [ParOldGen: 2762176K->1926377K(3520000K)] 3172222K->1926377K(13635584K) [PSPermGen: 166243K->166242K(262144K)], 4.1453870 secs] [Times: user=20.11 sys=0.03, real=4.14 secs] 2312.878: [Full GC [PSYoungGen: 580541K->0K(10115520K)] [ParOldGen: 3448192K->2398541K(4246336K)] 4028733K->2398541K(14361856K) [PSPermGen: 166243K->166243K(262144K)], 5.0057740 secs] [Times: user=25.06 sys=0.03, real=5.01 secs] 2406.074: [Full GC [PSYoungGen: 442182K->0K(9997056K)] [ParOldGen: 4059865K->2220928K(4308544K)] 4502047K->2220928K(14305600K) [PSPermGen: 166245K->166244K(262144K)], 4.6421270 secs] [Times: user=22.88 sys=0.04, real=4.65 secs] 2492.633: [Full GC [PSYoungGen: 551513K->0K(10058560K)] [ParOldGen: 4035620K->2219019K(4526080K)] 4587134K->2219019K(14584640K) [PSPermGen: 166249K->166249K(251264K)], 4.4630140 secs] [Times: user=22.36 sys=0.01, real=4.46 secs] 2551.866: [Full GC [PSYoungGen: 757661K->0K(9941888K)] [ParOldGen: 4285737K->2455810K(4991616K)] 5043398K->2455810K(14933504K) [PSPermGen: 166259K->166258K(239424K)], 4.5891680 secs] [Times: user=23.50 sys=0.03, real=4.59 secs] 2587.593: [Full GC [PSYoungGen: 180557K->0K(10140096K)] [ParOldGen: 4554339K->2111979K(4954688K)] 4734896K->2111979K(15094784K) [PSPermGen: 166264K->166264K(229056K)], 4.5708050 secs] [Times: user=22.55 sys=0.02, real=4.57 secs] 2717.263: [Full GC [PSYoungGen: 286857K->0K(9503424K)] [ParOldGen: 4726275K->2537156K(5506560K)] 5013132K->2537156K(15009984K) [PSPermGen: 166271K->166271K(219712K)], 6.0445180 secs] [Times: user=28.51 sys=0.03, real=6.04 secs] 2794.947: [Full GC [PSYoungGen: 601040K->0K(9763968K)] [ParOldGen: 5454180K->2849912K(6176064K)] 6055221K->2849912K(15940032K) [PSPermGen: 166272K->166272K(211968K)], 6.1950910 secs] [Times: user=30.35 sys=0.05, real=6.20 secs] 2841.383: [Full GC [PSYoungGen: 544562K->0K(10043968K)] [ParOldGen: 5736088K->3054858K(6731904K)] 6280651K->3054858K(16775872K) [PSPermGen: 166274K->166273K(204800K)], 6.5399570 secs] [Times: user=32.23 sys=0.04, real=6.54 secs] 6663.915: [Full GC [PSYoungGen: 1080358K->0K(10062592K)] [ParOldGen: 6735458K->2460384K(5908544K)] 7815817K->2460384K(15971136K) [PSPermGen: 166399K->166398K(199168K)], 7.2638840 secs] [Times: user=29.06 sys=0.06, real=7.27 secs]

Die Durchschnittszahlen für die alte und gegenwärtige Generation sind:

Generation

Durchschnitt

Alt

2282735 KB

Gegenwärtig

166227 KB

Hier besteht ein Widerspruch: Sofern die Applikation innerhalb der JVM mit aggressiven Caches läuft und die Cache-Größe proportional zur Datenstrukturgröße oder zur Größe der alten Generation ist, wird sich die Größe der alten Generation wahrscheinlich erhöhen, wenn die Datenstrukturgröße oder die alte Generation zunimmt. Das führt zu einer Inflation der Datenstrukturgröße anteilig zum Caching.

Faustregeln für die Generationengrößen

Es gibt eine Reihe von Faustregeln, um die Größe der verschiedenen Generationen festzulegen, die auf dem Speicherbedarf basieren:

  • Die maximale Datenstrukturgröße sollte zwischen dem drei- oder vierfachen der Datenstrukturgröße der alten Generation betragen
  • Die alte Generation sollte nicht kleiner sein als das eineinhalbfache der Datenstrukturgröße der alten Generation
  • Die gegenwärtige Generation sollte nicht kleiner sein als das eineinhalbfache der Datenstrukturgröße der gegenwärtigen Generation
  • Die neue Generation sollte nicht weniger als 10% der gesamten Datenstrukturgröße einnehmen. Dieser Punkt ist nur wichtig, wenn Sie die Größe der neuen Generation manuell festlegen.
  • Wenn Sie zur größtenteils simultan laufenden Speicherbereinigung wechseln, erhöhen Sie die Größe der alten und gegenwärtigen Generation um 20% (sofern das Inhaltsverwaltungssystem der gegenwärtigen Generation freigegeben ist)
  • Erwarten Sie nicht, dass Ihnen die Menge an physikalischem Speicher zur Verfügung steht, wenn Sie die Größe der JVM umstellen. (Die JVM verbraucht mehr Speicher als nur die Datenstruktur.) So vermeiden Sie eine Überlastung des virtuellen Speichers oder (im schlimmsten Fall) die Terminierung der JVM-Prozesse.
Festlegen der anfänglichen Datenstrukturgröße

Da Sie nun den Speicherbedarf kalkuliert haben, können Sie die anfängliche Datenstrukturgröße für folgende Tuning-Übung festsetzen:

Speicherbereinigungs-Parameter

Wert

VM-Flags

Datenstrukturgröße

4 x Datenstrukturgröße der alten Generation

-Xmx<n>[g|m|k]

Größe der alten Generation

1,5 x Datenstrukturgröße der gegenwärtigen Generation

-XX:MaxPermSize=<n>[g|m|k]

Bei Verwendung des Beispiels für den Speicherbedarf ergibt sich folgende anfängliche Datenstrukturgröße:

-Xmx9130942K -XX:MaxPermSize=249340K
Festlegung der Systemvoraussetzungen

Dieser Inhalt wurde zuletzt am 15.09.2017 aktualisiert.

Der Inhalt auf dieser Seite ist schon seit einer Weile nicht mehr aktualisiert worden. Das muss kein Nachteil sein. Oft überdauern unsere Seiten Jahre, ohne wirklich unnütz zu werden.

Alte Inhalte können falsch, irreführend oder überholt sein. Bitte nutzen Sie das Formular oder den Live-Chat auf dieser Seite oder kontaktieren Sie uns via E-Mail unter content@seibert.group, wenn Sie Zweifel, Fragen, Anregungen oder Änderungswünsche haben.