[JAVA] JVM-Leistungsoptimierung: Was ist Optimierung und wie erstellt man einen guten Plan?

Something went wrong Die Leistungsoptimierung für ** JVM ** ist eine systematische und komplexe Aufgabe. Dieser Artikel erklärt das Konzept und zeigt, wie Sie ** JVM ** -Parameter verwenden, um Ihre Anwendung zu optimieren.

** Dieser Blog ist eine Übersetzung aus der englischen Version. Sie können das Original von [hier] überprüfen (https://www.alibabacloud.com/blog/how-to-properly-plan-jvm-performance-tuning_594663). Einige maschinelle Übersetzungen werden verwendet. Wir würden uns freuen, wenn Sie auf Übersetzungsfehler hinweisen könnten. ** ** **

Leistungsoptimierungsebene

Um die Leistung des Systems zu verbessern, ist es notwendig, unter verschiedenen Gesichtspunkten und Ebenen zu optimieren. Die zu optimierenden Schichten sind wie folgt.

image.png

Wie Sie sehen, müssen zusätzlich zur JVM-Optimierung viele Ebenen optimiert werden. Beim Optimieren des Systems geht es nicht nur um das Optimieren der JVM. Vielmehr muss das gesamte System optimiert werden, um die Leistung des Systems zu verbessern. Dieser Artikel beschreibt nur die JVM-Optimierung. Andere Abstimmungsaspekte werden später diskutiert.

Nehmen Sie vor der JVM-Optimierung an, dass die Architektur und der Code Ihres Projekts optimiert sind oder dass dies die beste Architektur und der beste Code für Ihr aktuelles Projekt ist. Diese beiden Annahmen bilden die Grundlage für die JVM-Optimierung, und die Architekturoptimierung hat den größten Einfluss auf die Systemleistung. Sie können keinen qualitativen Sprung von einer Anwendung erwarten, die architektonisch fehlerhaft ist oder eine Codeoptimierung erfordert, indem nur eine JVM-Optimierung durchgeführt wird.

Darüber hinaus sollten Sie klare Ziele für die Leistungsoptimierung haben und die aktuellen Leistungsengpässe kennen, bevor Sie mit der Optimierung beginnen. Um Engpässe zu optimieren, führen Sie Stress- und Benchmark-Tests für Ihre Anwendung durch und verwenden Sie eine Vielzahl von Überwachungs- und Statistik-Tools, um festzustellen, ob Ihre optimierte Anwendung Ihren Zielen entspricht. müssen es tun.

JVM-Optimierungsschritte

Das ultimative Ziel der Optimierung besteht darin, sicherzustellen, dass Ihre Anwendung einen höheren Durchsatz bei niedrigsten Kosten für den Hardwareverbrauch aufweist. JVM-Tuning ist keine Ausnahme. Die JVM-Optimierung optimiert in erster Linie den Garbage Collector, um die Erfassungsleistung zu verbessern. So können Anwendungen, die auf einer VM ausgeführt werden, einen höheren Durchsatz erzielen und gleichzeitig eine geringere Latenz bei geringerer Speichernutzung erzielen. werde ich so machen. Beachten Sie, dass eine geringe Speichernutzung / geringe Latenz keine bessere Leistung bedeutet. Es ist wichtig, die beste Wahl zu treffen.

Leistungsmetriken

Um Leistungsengpässe zu finden und zu bewerten, müssen Sie die Definition von Leistungsmetriken kennen. Für die JVM-Optimierung müssen Sie die folgenden drei Definitionen kennen und diese Metriken als Grundlage für die Bewertung verwenden.

Prinzipien der Leistungsoptimierung

Während des Optimierungsprozesses helfen Ihnen die folgenden drei Prinzipien dabei, eine einfachere Optimierung der Speicherbereinigung zu implementieren, um die Leistungsanforderungen Ihrer gewünschten Anwendung zu erfüllen.

Verfahren zur Leistungsoptimierung

image.png

Die obige Abbildung zeigt die grundlegenden JVM-Optimierungsschritte für eine Anwendung. Es ist ersichtlich, dass die JVM-Optimierung eine kontinuierliche Konfigurationsoptimierung und mehrere Iterationen basierend auf Leistungstestergebnissen umfasst. Jeder der vorherigen Schritte kann mehrere Iterationen durchlaufen, bevor jede gewünschte Systemmetrik erfüllt ist. In einigen Fällen müssen die vorherigen Parameter möglicherweise mehrmals angepasst werden, um eine bestimmte Metrik zu erfüllen, und alle vorherigen Schritte müssen möglicherweise erneut getestet werden.

Darüber hinaus beginnt die Optimierung in der Regel mit der Erfüllung der Anforderungen an die Speichernutzung der Anwendung, gefolgt von Latenz und Durchsatz. Die Abstimmung sollte dieser Reihe von Schritten folgen. Die Reihenfolge dieser Abstimmungsschritte kann nicht umgekehrt werden. In den folgenden Abschnitten werden die einzelnen Optimierungsschritte anhand von Beispielen beschrieben.

Wählen Sie direkt den offiziell empfohlenen Servermodus in JDK 1.6 und höher aus, um die JVM auszuführen.

Verwenden Sie den JDK 1.6-1.8-Standardparallelkollektor als Garbage Collector. (Verwenden Sie parallelGC für jüngere Generationen, parallelOldGC für ältere Generationen).

Bestimmen der Speichernutzung

Es gibt zwei Dinge, die Sie wissen müssen, bevor Sie sich für die Speichernutzung entscheiden.

1, Anwendungsbetriebsphase 2, JVM-Speicherzuordnung

Betriebsphase

Die Funktionsweise der Anwendung wird in den folgenden drei Phasen erläutert.

--Initialisierung: Die JVM lädt die Anwendung und initialisiert das Hauptmodul und die Daten der Anwendung. --Stabilisierung: Die Anwendung wird seit langer Zeit ausgeführt und wird einem Stresstest unterzogen. Jeder Leistungsparameter befindet sich in einem stabilen Zustand. Die Kernfunktionen wurden mithilfe der JIT-Kompilierung ausgeführt und aufgewärmt. --Zusammenfassung: In der letzten Zusammenfassungsphase führen wir einige Benchmark-Tests durch, um die entsprechenden Berichte zu erstellen. Dieser Phase sollte keine besondere Aufmerksamkeit geschenkt werden. Die Speichernutzung und die Größe der aktiven Daten sollten während der Anwendungsstabilisierungsphase und nicht während der Projektstartphase festgelegt werden. Bevor wir erklären, wie die Speichernutzung bestimmt wird, schauen wir uns zunächst die JVM-Speicherzuordnung an.

JVM-Speicherzuordnung und -Parameter

image.png

Der Haupt-JVM-Heap-Bereich besteht aus jüngeren Generationen, älteren Generationen und persistenten Generationen. Die Größe der jüngeren Generation, die Größe der älteren Generation und die Größe der persistenten Generation bilden die Gesamtgröße des Heapspeichers. Wie ein bestimmtes Objekt angetrieben wird, wird hier nicht erläutert. Nun wollen wir sehen, wie der folgende JVM-Befehl die Heap-Größe angibt. Wenn die folgenden Parameter nicht zur Angabe der Heap-Größe verwendet werden, wählt die virtuelle Maschine automatisch den entsprechenden Wert aus, der je nach Systemaufwand automatisch angepasst werden kann.

image.png

Wenn Sie Bedenken hinsichtlich des Leistungsaufwands haben, kann nur FullGC die Größe der permanenten Generierung implementieren. Setzen Sie daher die Anfangsgröße und die maximale Größe der permanenten Generierung nach Möglichkeit auf denselben Wert.

Berechnen Sie die Größe der aktiven Daten

Gehen Sie folgendermaßen vor, um die Größe der aktiven Daten zu berechnen:

image.png

Wie bereits erwähnt, sollte die Größe der aktiven Daten daran gemessen werden, wie viel Speicherplatz der Java-Heap von Daten belegt wird, die seit Beginn der Stabilitätsphase der Anwendung lange Zeit aktiv waren.

Stellen Sie bei der Berechnung der aktiven Datengröße sicher, dass Sie die folgenden Anforderungen erfüllen:

Wann befindet sich die Anwendung in der stabilen Phase?

Nach ausreichender Belastung befindet sich die Anwendung nur dann in der Stabilitätsphase, wenn sie eine Arbeitslast erreicht, die die Geschäftsanforderungen während der Hauptgeschäftszeiten in einer Produktionsumgebung erfüllt und nach der Spitzenzeit stabil bleibt. .. Stresstests sind daher unerlässlich, um festzustellen, ob eine Anwendung eine stabile Phase erreicht hat. Die Durchführung von Stresstests für Ihre Anwendung geht über den Rahmen dieses Artikels hinaus. Ich werde diese Frage später in einem anderen Artikel diskutieren.

Nachdem Sie festgestellt haben, dass sich die Anwendung in einem stabilen Stadium befindet, beachten Sie das GC-Protokoll der Anwendung, insbesondere das vollständige GC-Protokoll.

GC log directive: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>

GC-Protokolle sind der beste Weg, um die für die Optimierung erforderlichen Informationen zu sammeln. Sie können das Problem herausfinden, indem Sie die GC-Protokollierung auch in der Produktionsumgebung aktivieren. Durch Aktivieren der GC-Protokollierung können Sie eine Fülle von Daten bereitstellen und gleichzeitig die Auswirkungen auf die Leistung minimieren.

FullGC-Protokoll ist erforderlich. Wenn das FullG-Protokoll nicht verfügbar ist, erzwingen Sie den Anruf mit einem Überwachungstool oder lösen Sie das Protokoll mit dem folgenden Befehl aus.

jmap -histo:live pid

Wenn während der stabilen Periode die vollständige GC aktiviert ist, können die folgenden Informationen erhalten werden.

image.png

Aus dem oben genannten GC-Protokoll können Sie eine grobe Schätzung der Heap-Nutzung und der GC-Zeit für die gesamte Anwendung während eines vollständigen GC erhalten. Um eine genauere Schätzung zu erhalten, sammeln Sie die Informationen mehrmals und berechnen Sie den Durchschnitt. Alternativ kann es mit dem längsten FullGC geschätzt werden.

In der obigen Abbildung sind 93168 KB (ca. 93 MB) des Raums der alten Generation nach vollständiger GC belegt. Betrachten Sie diese Datenmenge als aktive Daten im Bereich der alten Generation.

Andere Heap-Speicherplätze werden gemäß den folgenden Regeln zugewiesen.

image.png

Ausgehend von den obigen Regeln und den FullGC-Informationen in der vorherigen Abbildung kann der Anwendungsheapspeicher wie folgt geplant werden.

Java-Heap-Speicherplatz: 373 MB = 93168 KB (Speicherplatz der alten Generation) x 4

Speicherplatz für junge Generationen: 140 MB = 93168 KB (Speicherplatz für alte Generationen) x 1,5

Speicherplatz für permanente Generierung: 5 MB = 3135 KB (Speicherplatz für permanente Generierung) x 1.

Speicherplatz der alten Generation: 233 MB = 373 MB (Heap-Speicherplatz) -140 MB (Speicherplatz der jungen Generation)

Die entsprechenden Anwendungsstartparameter sind

java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m

Latenzabstimmung

Nachdem Sie die aktive Datengröße für Ihre Anwendung ermittelt haben, müssen Sie die Latenz einstellen. Zu diesem Zeitpunkt können Größe und Latenz des Heapspeichers die Anforderungen der Anwendung nicht erfüllen. Daher sollte die Anwendung basierend auf den tatsächlichen Anforderungen der Anwendung debuggt werden.

Während dieser Phase müssen Sie möglicherweise Ihre Konfiguration der Heap-Größe erneut optimieren, die GC-Dauer und -Häufigkeit bewerten und entscheiden, ob Sie zu einem anderen Garbage Collector wechseln müssen.

Anforderungen an die Systemlatenz

Vor dem Optimieren müssen Sie wissen, welche Latenzanforderungen Ihr System hat und welche Metriken für die Latenz optimiert werden können.

Achten Sie bei den oben genannten Metriken besonders auf die durchschnittliche Ausfallzeit und die maximale Pausenzeit. Diese beiden Metriken sind für die Benutzererfahrung sehr wichtig.

Basierend auf den oben genannten Anforderungen müssen Sie die folgenden Daten erhalten:

Optimierte Größe der jungen Generation

image.png

Im vorhergehenden GC-Protokoll beträgt die durchschnittliche Dauer des Neben-GC beispielsweise 0,069 Sekunden, und der Neben-GC tritt alle 0,389 Sekunden einmal auf.

Die durchschnittliche Ausfallzeit beträgt 50 ms und die aktuelle Dauer (69 ms) ist eindeutig zu lang, um angepasst zu werden.

Wir haben festgestellt, dass je kleiner der Raum in der ** jungen Generation ** ist, desto länger und seltener wird der MinorGC dauern.

Um die Dauer zu verkürzen, sollten Sie den Raum der jungen Generation verkleinern.

Um die Frequenz zu verringern, müssen Sie den Raum der jungen Generation vergrößern.

Lassen Sie beim Ändern der Größe des Raums der jungen Generation nach Möglichkeit die Größe des Raums der alten Generation, um die Auswirkungen der Größenänderung des Raums der jungen Generation auf andere Abschnitte zu minimieren.

Wenn Sie beispielsweise die Größe des Raums für junge Generationen um 10% reduzieren, ändern Sie nicht die Größe des Raums für alte Generationen und des Raums für permanente Generationen. Die Parameter nach der Optimierung in diesem Schritt sind wie folgt.

java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m

The size of the young generation is changed from 140 MB to 126 MB; the heap size is changed accordingly; the old generation has no changes at this point.

Optimieren Sie die Größe von ** Old Generation **

Wie im vorherigen Schritt müssen wir vor der Optimierung einige Daten aus dem GC-Protokoll abrufen. Dieser Schritt konzentriert sich auf die Dauer und Häufigkeit von FullGC.

image.png

Die folgenden Informationen können der vorherigen Abbildung entnommen werden.

The average FullGC frequency is 1 FullGC every 5.8s.

The average FullGC duration is 0.14s.

(This is only a test. FullGC lasts longer in real projects.)

Objektförderungsrate

Kann bewertet werden, ob kein FullGC-Protokoll vorhanden ist? Es ist möglich, die Aktionsrate für die Bewertung zu verwenden.

Im obigen Startparameter beträgt die Größe der alten Generation beispielsweise 233 MB.

Wie lange es dauert, diese 233 MB freien Speicherplatz zu belegen, hängt von der Beförderungsrate von der jungen zur alten Generation ab.

Werbeverwendung der alten Generation = Java-Heap-Nutzung nach jeder Nutzung der MinorGC-Young-Generation nach MinorGC

Objekt-Promotion-Rate = Durchschnittswert (jedes Mal die Nutzung der alten Generation fördern) / Speicherplatz der alten Generation

Mit der Objekt-Promotion-Rate können Sie die Anzahl der kleineren GCs berechnen, die in der alten Generation benötigt werden, um Platz zu belegen, und die ungefähre Dauer eines vollständigen GC.

Lassen Sie mich Ihnen ein Beispiel geben.

image.png

In der obigen Abbildung wird es wie folgt beschrieben.

After the first minor GC, the usage of the old generation space is 8 KB (13740 KB - 13732 KB).

After the second minor GC, the usage of the old generation space is 4489 KB (22394 KB - 17905 KB).

After the third minor GC, the usage of the old generation space is 16822 KB (34739 KB - 17917 KB).

After the fourth minor GC, the usage of the old generation space is 30230 KB (48143 KB - 17913 KB).

After the fifth minor GC, the usage of the old generation space is 44195 KB (62112 KB - 17917 KB).

Promotion-Nutzung der älteren Generation nach jedem kleinen GC

Between the second and the first minorGCs: 4481 KB

Between the third and the second minorGCs: 12333 KB

Between the fourth and the third minorGCs: 13408 KB

Between the fifth and the fourth minorGCs: 13965 KB

Nach der Berechnung können Sie die folgenden Informationen erhalten.

The average usage promotion for each minorGC is 12211 KB (about 12 MB).

In the preceding figure, the minorGC happens once every 213ms on average.

Promotion rate = 12211 KB/213ms = 57 KB/ms

It takes about 4.185s (233*1024/57 = 4185ms) to fully occupy 233 MB of the old generation space.

Die schlechteste volle GC-Frequenz kann unter Verwendung der beiden oben beschriebenen Verfahren geschätzt werden. Sie können die Frequenz von Full GC anpassen, indem Sie die Größe der vorherigen Generation ändern. Wenn der vollständige GC zu lang ist, um die Mindestlatenzanforderungen der Anwendung zu erfüllen, müssen Sie möglicherweise den Garbage Collector wechseln. Im nächsten Artikel wird beschrieben, wie Sie zu einem anderen Garbage Collector wechseln (z. B. Current Mark Sweep, Wechsel zu CMS usw.). CMS-Tuning ist etwas anders.

Durchsatz optimieren

Nachdem wir die oben genannten Abstimmungsschritte durchlaufen haben, befinden wir uns endlich im letzten Abstimmungsschritt. In diesem Schritt führen Sie einen Durchsatztest für die vorherigen Ergebnisse durch und nehmen Feineinstellungen vor.

Das gesamte Tuning basiert hauptsächlich auf den Durchsatzanforderungen Ihrer Anwendung. Die Anwendung muss über eine umfassende Durchsatzmetrik verfügen, die aus den allgemeinen Anwendungsanforderungen und Tests abgeleitet wird. Die Optimierung kann beendet werden, wenn der Anwendungsdurchsatz das erwartete Durchsatzziel erreicht oder überschreitet.

Wenn Sie die Durchsatzziele Ihrer Anwendung nach der Optimierung immer noch nicht erreichen können, sollten Sie Ihre Durchsatzanforderungen überprüfen und beurteilen, wie groß die Lücke zwischen Ihrem aktuellen Durchsatz und Ihren Zielen ist. Wenn die Lücke etwa 20% beträgt, können Sie die Parameter ändern, um den Speicher zu vergrößern und die Anwendung erneut zu debuggen. Wenn die Lücke zu groß ist, sollten Sie prüfen, ob die Entwurfs- und Durchsatzziele mit den Durchsatzzielen übereinstimmen, und sie aus einer anwendungsweiten Perspektive neu bewerten.

Für Garbage Collectors besteht das Ziel der Durchsatzoptimierung darin, das Auftreten von vollständigem GC und Stop-The-World-CMS zu reduzieren oder zu vermeiden. Beide Garbage Collector-Methoden können zu einem verringerten Anwendungsdurchsatz führen. Versuchen Sie, während der MinorGC-Phase so viele Objekte wie möglich zu recyceln, um zu verhindern, dass sie schnell zu älteren Generationen befördert werden.

Fazit

Plumbr untersuchte die Verwendung spezifischer Müllsammler anhand von 84.936 Fällen. Von den Fällen, in denen der Garbage Collector explizit angegeben ist, wird in 13% der Fälle der Concurrent Mark Sweep (CMS) -Kollektor am häufigsten verwendet. In den meisten Fällen wurde jedoch nicht der optimale Garbage Collector ausgewählt. Dieser Mehrheitsfall macht etwa 87% aus.

image.png

Das Einstellen der JVM ist eine systematische und komplexe Aufgabe. Im Moment ist die automatische Abstimmung unter der JVM sehr gut, und das Einstellen grundlegender Anfangsparameter kann einen stabilen Betrieb für gängige Anwendungen gewährleisten. Einige Teams priorisieren möglicherweise nicht die Anwendungsleistung. In diesem Fall reicht der Standard-Garbage Collector normalerweise aus, um die gewünschten Anforderungen zu erfüllen. Die Abstimmung sollte auf Ihrer eigenen Situation basieren.

Recommended Posts

JVM-Leistungsoptimierung: Was ist Optimierung und wie erstellt man einen guten Plan?
Was ist ein unveränderliches Objekt? [Erklärung, wie man macht]
So erstellen Sie einen Java-Container
So erstellen Sie einen JDBC-Treiber
So erstellen Sie einen Begrüßungsbildschirm
So erstellen Sie ein Jenkins-Plug-In
Wie erstelle ich ein Maven-Projekt?
So erstellen Sie ein Java-Array
So identifizieren Sie den Pfad, auf dem leicht Fehler gemacht werden können
So erstellen Sie eine Java-Kalenderzusammenfassung
Wie erstelle ich einen Discord Bot (Java)
So erstellen Sie eine App mit einem Plug-In-Mechanismus [C # und Java]
Einführung in rekursive Funktionen: Was ist eine rekursive Funktion?
So erstellen Sie eine leichte JRE für den Vertrieb
Suchen Sie einen geeigneten Wert für eine Methode und machen Sie sie zu einem ValueObject
Wie erstelle ich ein Vagrant Plugin, das Sie gelernt haben, als Sie Vagrant-Mutagen gegabelt und veröffentlicht haben?
[Rails] [Docker] Kopieren und Einfügen ist in Ordnung! So erstellen Sie eine Rails-Entwicklungsumgebung mit Docker
So funktioniert JavaScript auf einer bestimmten Seite
So konvertieren Sie A in a und a in A mit logischem Produkt und Summe in Java
Wie man einen Cache erstellt, ohne zu viel nachzudenken
Wie erstelle ich einen MOD für Slay the Spire?
Was ist ein Konstruktor?
Was ist ein Stream?
Wie man ein schattiertes Glas macht
Was ist ein Servlet?
Ruby mit AtCoder lernen 13 So erstellen Sie ein zweidimensionales Array
Was ist in "Java 8 bis Java 11" passiert und wie wird eine Umgebung erstellt?
Was ist das? Entdecken Sie, wie jeder ein Programm schreiben kann
So entwickeln und registrieren Sie eine Sota-App in Java
So verbinden Sie eine Tabelle ohne DBFlute und SQL
So erstellen und starten Sie eine Docker-Datei für Payara Micro
Was ist eine Wrapper-Klasse?
Was ist ein boolescher Typ?
Java - So erstellen Sie JTable
Was ist ein aussagekräftiger Kommentar?
Wie hinterlasse ich einen Kommentar?
Was ist eine JAR-Datei?
Was ist eine Java-Sammlung?
Was ist ein Lambda-Ausdruck?
[Schienen] Wie man Samen macht
Einführung in Ratpack (1) - Was ist Ratpack?
Was ist Fat⁉ enum?
So fügen Sie ein Video ein
So erstellen Sie eine Methode
Was sind Microservices und Microservices Frameworks?
So erstellen Sie mit SPRING INITIALIZR einen Hinadan für ein Spring Boot-Projekt
[Java] Verwendung statischer Modifikatoren (Was sind statische endgültige und statische Importe)
So laden Sie eine Spring-Upload-Datei und zeigen ihren Inhalt an
So lesen Sie eine Datei und behandeln sie als Standardeingabe
Was ist der Unterschied zwischen einem Webserver und einem Anwendungsserver?
So stellen Sie sicher, dass CsrfRequestDataValueProcessor und der ursprüngliche RequestDataValueProcessor beim Spring Boot nebeneinander existieren
Ich habe versucht, einen Numeron zu erstellen, der mit Ruby nicht gut ist
[Swift5] So kommunizieren Sie von ViewController zu Model und übergeben einen Wert
Wie man einen revolutionären Diamanten mit Java für Aussage macht wwww