Bauen Sie einen gusseisernen Walker auf Spring Boot und AppCDS


AppCDS (Application Class Data Sharing) - JVM-Funktion zur Beschleunigung des Starts und zum Speichern von Speicher. Nachdem es in JDK 1.5 (2004) in den Kinderschuhen von HotSpot aufgetaucht war, blieb es lange Zeit sehr begrenzt und sogar teilweise kommerziell. Erst mit OpenJDK 10 (2018) wurde es bloßen Sterblichen zur Verfügung gestellt und gleichzeitig der Anwendungsbereich erweitert. Und kürzlich veröffentlichtes Java 13 hat versucht, diese Anwendung zu vereinfachen.


Die Idee von AppCDS besteht darin, einmal geladene Klassen zwischen Instanzen derselben JVM auf demselben Host zu „teilen“. Es scheint, dass dies für Microservices großartig sein sollte, insbesondere für die "Broiler" auf Spring Boot mit ihren Tausenden von Bibliotheksklassen, da diese Klassen jetzt nicht bei jedem Start jeder JVM-Instanz geladen (analysiert und überprüft) werden müssen und nicht im Speicher dupliziert werden. Dies bedeutet, dass der Start schneller und der Speicherverbrauch geringer sein sollte. Wunderbar, nicht wahr?


Alles ist so, alles ist so. Aber wenn Sie, der Odnokhabryanin, früher nicht an die Boulevardschilder, sondern an bestimmte Zahlen und Beispiele glaubten, dann willkommen bei kat - versuchen wir herauszufinden, wie es wirklich ist ...


Anstelle eines Haftungsausschlusses


Vorher ist kein Leitfaden zur Verwendung von AppCDS, sondern eine Zusammenfassung der Ergebnisse einer kleinen Studie. Ich war daran interessiert zu verstehen, wie diese JVM-Funktion in meinem Arbeitsprojekt anwendbar ist, und habe versucht, sie aus der Sicht eines Unternehmensentwicklers zu bewerten, indem ich das Ergebnis in diesem Artikel darlegte. Dies beinhaltete keine Themen wie die Verwendung von AppCDS im Modulpfad, die Implementierung von AppCDS auf anderen virtuellen Maschinen (nicht HotSpot) und die Komplikationen bei der Verwendung von Containern. Es gibt jedoch einen theoretischen Teil zum Erkunden des Themas sowie einen experimentellen Teil, der geschrieben wurde, damit Sie die Erfahrung selbst wiederholen können. Keines der Ergebnisse wurde bisher in der Produktion angewendet, aber wer weiß, wie es morgen sein wird ...


Theorie


Eine kurze Einführung in AppCDS


Die Bekanntschaft mit diesem Thema ist Ihnen möglicherweise aus verschiedenen Quellen gekommen, zum Beispiel:


  • in einem Artikel von Nikolai Parlog (einschließlich Java 13 Brötchen, aber ohne Spring Boot)
  • in einem Bericht und Artikel von Volker Simonis (ohne Java 13, aber mit Details)
  • in einem Bericht des Autors dieser Zeilen (ohne Java 13, jedoch mit Schwerpunkt auf Spring Boot)

Um mich nicht auf Nacherzählungen einzulassen, werde ich nur einige Punkte hervorheben, die für diesen Artikel wichtig sind.


Erstens ist AppCDS eine Erweiterung der CDS-Funktion, die seit langem in HotSpot verfügbar ist. Die Essenz lautet wie folgt:



Um beide Ideen zum Leben zu erwecken, müssen Sie (allgemein) Folgendes tun:


  1. Rufen Sie eine Liste der Klassen ab, die Sie für Anwendungsinstanzen freigeben möchten
  2. Führen Sie diese Klassen in einem Archiv zusammen, das für die Speicherzuordnung geeignet ist
  3. Verbinden Sie das Archiv beim Start mit jeder Instanz der Anwendung

Es scheint, dass der Algorithmus nur 3 Schritte umfasst - nehmen Sie es und machen Sie es. Aber hier beginnen die Nachrichten, alle möglichen Dinge.


Das Schlimme ist, dass im schlimmsten Fall jedes dieser Elemente zu mindestens einem JVM-Start mit eigenen spezifischen Optionen wird, was bedeutet, dass der gesamte Algorithmus ein subtiles Jonglieren derselben Art von Optionen und Dateien ist. Das klingt nicht sehr vielversprechend, oder?


Es gibt jedoch gute Nachrichten: Die Arbeiten zur Verbesserung dieses Algorithmus dauern an , und mit jeder Version von Java wird die Anwendung einfacher. Also zum Beispiel:


  • In OpenJDK 10 und 11 können Sie Schritt 1 überspringen, wenn Sie nur die wichtigsten JDK-Klassen freigeben möchten, da diese bereits für uns kompiliert und in $JAVA_HOME\lib\classlist (~ 1200 Stk.) Eingefügt wurden.
  • In OpenJDK 12 können Sie Schritt 2 überspringen, da das Verteilungsarchiv neben der Liste der Klassen auch ein vorgefertigtes Archiv enthält, das sofort verwendet wird und keine explizite Verbindung erfordert.
  • Für den Fall, dass Sie alles andere teilen möchten (und normalerweise nur möchten)
    OpenJDK 13 bietet dynamische CDS-Archive - Archive, die während des Betriebs der Anwendung gesammelt und gespeichert werden, wenn sie besetzt ist. Auf diese Weise können Sie die Punkte 1 und 2 zu einem nicht zu verwirrenden Punkt zusammenfassen (obwohl nicht alles so einfach ist, aber dazu später mehr).

Unabhängig davon, wie der Prozess der Vorbereitung von AppCDS abläuft, stehen die drei oben aufgeführten Schritte immer dahinter, nur in einigen Fällen sind sie verschleiert.


Wie Sie wahrscheinlich bemerkt haben, beginnen viele Anwendungsklassen mit dem Aufkommen von AppCDS ein Doppelleben: Sie leben gleichzeitig an ihren früheren Orten (meistens JAR-Dateien) und in einem neuen gemeinsam genutzten Archiv. Gleichzeitig ändert / entfernt / ergänzt der Entwickler sie weiterhin an derselben Stelle, und die JVM übernimmt sie bei der Arbeit von der neuen. Man muss kein Wahrsager sein, um die Gefahr einer solchen Situation zu erkennen: Wenn nichts unternommen wird, werden früher oder später Kopien der Klassen korrodieren und wir werden viele Reize der typischen „JAR-Hölle“ bekommen. Es ist klar, dass die JVM Klassenänderungen nicht verhindern kann, aber in der Lage sein sollte, eine zeitliche Diskrepanz zu erkennen. Es ist jedoch eine Idee, dies durch paarweisen Vergleich von Klassen zu tun, selbst durch Prüfsummen. Dies kann den Rest der Produktivitätsgewinne zunichte machen. Dies ist wahrscheinlich der Grund, warum die JVM-Ingenieure nicht die einzelnen Klassen als Vergleichsobjekt, sondern den gesamten Klassenpfad ausgewählt haben. In der AppCDS-Dokumentation heißt es: „Der Klassenpfad beim Erstellen eines gemeinsam genutzten Archivs sollte derselbe (oder zumindest ein Präfix) sein wie bei nachfolgenden Starts der Anwendung.“


Beachten Sie, dass der zur Erstellungszeit des Archivs verwendete Klassenpfad mit dem zur Laufzeit verwendeten Klassenpfad (oder einem Präfix davon) identisch sein muss.

Dies ist jedoch keine eindeutige Aussage, da, wie Sie sich erinnern, ein Klassenpfad auf verschiedene Arten gebildet werden kann, z.


  • Lesen von .class Dateien aus kompilierten Paketverzeichnissen,
    B. java com.example.Main
  • Scannen von Verzeichnissen mit JAR-Dateien bei Verwendung von Platzhaltern,
    B. java -cp mydir/* com.example.Main
  • explizite Auflistung von JAR- und / oder ZIP-Dateien,
    B. java -cp lib1.jar;lib2.jar com.example.Main

(Dies schließt nicht die Tatsache ein, dass der Klassenpfad auch anders festgelegt werden kann, z. B. über die JVM-Optionen -cp/-classpath/--class-path , die Umgebungsvariable CLASSPATH oder das Attribut der zu startenden JAR-Datei für den Class-Path )


Von diesen Methoden wird in AppCDS nur eine unterstützt - die explizite Aufzählung von JAR-Dateien. Anscheinend waren die HotSpot JVM-Ingenieure der Ansicht, dass der Vergleich von Klassenpfaden im AppCDS-Archiv und in der gestarteten Anwendung nur dann schnell genug und zuverlässig wäre, wenn sie so klar wie möglich angegeben würden - mit einer üblichen vollständigen Liste.


CDS / AppCDS unterstützt die Archivierung von Klassen nur aus JAR-Dateien.

Es ist wichtig anzumerken, dass diese Aussage nicht rekursiv ist, d.h. gilt nicht für JAR-Dateien in JAR-Dateien (es sei denn, es handelt sich um Dynamic CDS, siehe unten). Dies bedeutet, dass die üblichen JAR-Puppen von Spring Boot mit regulären AppCDS nicht so funktionieren. Sie müssen sich setzen.


Ein weiterer Haken in der Arbeit von CDS ist, dass gemeinsam genutzte Archive mit festen Adressen (normalerweise ab 0x800000000 ) auf den Speicher projiziert werden. Dies ist an sich nicht schlecht, aber da die Adressraum-Layout-Randomisierung (ASLR) auf den meisten Betriebssystemen standardmäßig aktiviert ist, kann der erforderliche Speicherbereich teilweise belegt sein. Was die HotSpot JVM in diesem Fall tut, ist die spezielle Option -Xshare , die drei Werte unterstützt:


  • -Xshare:on - CDS / AppCDS erzwingen; Wenn der Bereich belegt ist, wird die JVM mit einem Fehler beendet. Dieser Modus wird für die Verwendung in der Produktion nicht empfohlen , da dies zu sporadischen Abstürzen beim Starten von Anwendungen führen kann.
  • -Xshare:off - (Sie) schalten CDS / AppCDS; deaktiviert die Verwendung gemeinsam genutzter Daten vollständig (einschließlich eingebetteter Archive)
  • -Xshare:auto - das Standardverhalten der JVM, wenn sie im Falle der Unmöglichkeit, den erforderlichen Speicherbereich zuzuweisen, leise aufgibt und die Klassen wie gewohnt lädt

Zum Zeitpunkt des Schreibens dieses Artikels arbeitet Oracle nur daran , solche Probleme zu beheben, aber eine Versionsnummer wurde noch nicht zugewiesen.


Diese Optionen sind für uns später teilweise nützlich, aber jetzt schauen wir uns ...


AppCDS-Anwendungen


Es gibt verschiedene Möglichkeiten mit AppCDS. ruiniere dein Leben Optimieren Sie die Arbeit von Microservices. Sie unterscheiden sich stark in ihrer Komplexität und ihrem potenziellen Gewinn. Daher ist es wichtig, sofort zu entscheiden, welche später besprochen wird.


Am einfachsten ist es, nicht einmal AppCDS, sondern nur CDS zu verwenden. In diesem Fall gelangen nur Plattformklassen in das gemeinsam genutzte Archiv (siehe "Eine kurze Einführung in AppCDS"). Wir werden diese Option sofort löschen, da sie bei Anwendung auf Microservices in Spring Boot zu wenig Gewinn bringt. Dies lässt sich am Anteil eines gemeinsamen Mikrodienstes an der Anzahl der gemeinsam genutzten Klassen in ihrer allgemeinen Verteilung ablesen (siehe grünes Segment):



Komplexer, aber vielversprechender ist die Verwendung von vollwertigem AppCDS, dh die Aufnahme von Bibliotheks- und Anwendungsklassen in dasselbe Archiv. Dies ist eine ganze Familie von Optionen, die aus Kombinationen der Anzahl der teilnehmenden Anwendungen und der Anzahl der Instanzen abgeleitet werden. Das Folgende sind subjektive Einschätzungen des Autors zu den Vorteilen und Schwierigkeiten verschiedener Anwendungen von AppCDS.


NrAnwendungenInstanzenCPU-GewinnRAM GewinnSchwierigkeit
1EinsEins+±Niedrig
2EinsEin paar++++Niedrig
3Ein paarEinzeln++++Hoch
4Ein paarEin paar++++++Hoch

Achten Sie darauf:


  • In der Anwendung auf eine Anwendung in einer Instanz (Nr. 1) kann sich der Speichergewinn als Null oder sogar negativ herausstellen (insbesondere bei Messungen unter Windows ).
  • Das Erstellen des richtigen freigegebenen Archivs erfordert Aktionen, deren Komplexität nicht davon abhängt, wie viele Kopien die Anwendung dann gestartet wird (vergleichen Sie die Optionspaare Nr. 1-2 und Nr. 3-4).
  • Gleichzeitig führt der Übergang von einer Instanz zu mehreren offensichtlich zu einer Gewinnsteigerung für beide Indikatoren, hat jedoch keinen Einfluss auf die Komplexität der Vorbereitung.

In diesem Artikel werden wir nur die Option Nr. 2 (bis Nr. 1) erreichen, da sie für eine enge Bekanntschaft mit AppCDS einfach genug ist und wir nur ohne zusätzliche Tricks das kürzlich veröffentlichte dynamische CDS-Archiv JEP-350 verwenden können , das ich in Aktion fühlen möchte.


Dynamische CDS-Archive


Das dynamische CDS-Archiv JEP-350 , eine der wichtigsten Innovationen von Java 13, soll die Verwendung von AppCDS vereinfachen. Um die Vereinfachung zu spüren, müssen Sie zuerst die Komplexität verstehen. Ich möchte Sie daran erinnern, dass der klassische „saubere“ Algorithmus zum Anwenden von AppCDS aus drei Schritten besteht: (1) Abrufen einer Liste gemeinsam genutzter Klassen, (2) Erstellen eines Archivs daraus und (3) Ausführen der Anwendung mit verbundenem Archiv. Von diesen Schritten ist nur der dritte tatsächlich nützlich, der Rest ist nur die Vorbereitung dafür. Und obwohl das Abrufen einer Liste von Klassen (Schritt 1) ​​sehr einfach erscheint (in einigen Fällen ist dies nicht einmal erforderlich), erweist es sich bei der Arbeit mit nicht trivialen Anwendungen als am schwierigsten, insbesondere im Hinblick auf Spring Boot. JEP-350 wird also nur benötigt, um diesen Schritt zu eliminieren oder vielmehr zu automatisieren. Die Idee ist, dass die JVM selbst eine Liste der Klassen erstellt, die die Anwendung benötigt, und dann selbst das sogenannte „dynamische“ Archiv daraus bildet. Stimmen Sie zu, es klingt gut. Der Haken ist jedoch, dass jetzt unklar wird, wann die Ansammlung von Klassen beendet und diese im Archiv abgelegt werden sollen. Zuvor haben wir im klassischen AppCDS einen solchen Moment selbst ausgewählt und konnten sogar zwischen diesen Aktionen wechseln, um etwas in der Liste der Klassen zu ändern, bevor wir es in ein Archiv verwandeln. Jetzt geschieht dies automatisch und nur zu einem Zeitpunkt, für den die JVM-Ingenieure möglicherweise die einzige Kompromissoption gewählt haben - das regelmäßige Herunterfahren der JVM. Dies bedeutet, dass das Archiv erst erstellt wird, wenn die Anwendung gestoppt wird. Diese Lösung hat einige wichtige Konsequenzen:


  • Im Falle eines JVM-Absturzes wird das Archiv nicht erstellt, egal wie wunderbar die Liste der bis dahin angesammelten Klassen wäre (Sie können sie später nicht mit regulären Mitteln extrahieren).
  • Das Archiv wird nur aus den Klassen erstellt, die während der Anwendungssitzung geladen werden konnten. Für Webanwendungen bedeutet dies, dass das Erstellen eines Archivs durch Starten und Stoppen direkt dort nicht korrekt ist, da dann viele wichtige Klassen nicht in das Archiv gelangen. Es ist erforderlich, mindestens eine HTTP-Anforderung an die Anwendung auszuführen (und es ist besser, sie in allen Szenarien ordnungsgemäß auszuführen), damit alle Klassen, die sie tatsächlich verwendet, geladen werden.

Ein wichtiger Unterschied zwischen dynamischen und statischen Archiven besteht darin, dass sie immer ein „Add-On“ gegenüber statischen Basisarchiven darstellen. Diese Archive können entweder in das Java Distribution Kit integriert oder auf klassische 3-Schritt-Weise separat erstellt werden.


Syntaktisch gesehen bedeutet die Verwendung von Dynamic CDS Archives zwei JVM-Starts mit zwei Optionen:


  1. Testlauf mit der Option -XX:ArchiveClassesAtExit=archive.jsa , an dessen Ende ein dynamisches Archiv erstellt wird (Sie können einen beliebigen Pfad und Namen angeben).
  2. Nützlicher Start mit der Option -XX:SharedArchiveFile=archive.jsa , die das zuvor erstellte Archiv verwendet

Die zweite Option unterscheidet sich nicht vom Verbinden eines regulären statischen Archivs. Befindet sich das statische Basisarchiv plötzlich nicht mehr am Standardspeicherort (innerhalb des JDK), enthält diese Option möglicherweise auch eine Angabe des Pfads dazu, z. B.:


 -XX:SharedArchiveFile=base.jsa:dynamic.jsa 

(Unter Windows muss das Pfadtrennzeichen das Zeichen ";" sein.)


Jetzt wissen Sie genug über AppCDS, damit Sie es in Aktion betrachten können.


Übe


Experimentelles Kaninchen


Damit unsere Anwendung von AppCDS in der Praxis nicht auf eine typische HelloWorld beschränkt ist, werden wir die eigentliche Anwendung auf Spring Boot als Grundlage nehmen. Meine Kollegen und ich müssen häufig Anwendungsprotokolle auf Remote-Testservern und "live" ansehen, so wie sie geschrieben wurden. Um dies zu verwenden, ist ein vollwertiger Protokollaggregator (wie ELK) oft nicht geeignet. Das endlose Herunterladen von Protokolldateien - für eine lange Zeit und das Betrachten der grauen Konsolenausgabe von tail ist deprimierend. Aus diesem Grund habe ich eine Webanwendung erstellt, mit der alle Protokolle in Echtzeit direkt an den Browser ausgegeben, Linien nach Wichtigkeitsgrad koloriert (gleichzeitig XML formatiert), mehrere Protokolle zu einem zusammengefasst und andere Tricks ausgeführt werden können. Es heißt ANALOG (z. B. ein „Log Analyzer“, obwohl dies nicht der Fall ist) und liegt auf GitHub . Klicken Sie auf den Screenshot, um ihn zu vergrößern:



Technisch gesehen ist dies eine Anwendung für Spring Boot + Spring Integration, unter deren Haube tail , docker und kubectl (um Protokolle von Dateien, Docker-Containern bzw. Kubernetes-Ressourcen zu unterstützen). Es kommt in Form der klassischen "dicken" Spring Boot JAR-Datei. Zur Laufzeit hängen 10K-Klassen im Anwendungsspeicher, von denen die überwiegende Mehrheit Spring- und JDK-Klassen sind. Offensichtlich ändern sich diese Klassen ziemlich selten, was bedeutet, dass sie in ein gemeinsam genutztes Archiv gestellt und in allen Instanzen der Anwendung wiederverwendet werden können, wodurch Speicher und CPU gespart werden.


Einzelversuch


Wenden wir nun das vorhandene Wissen über Dynamic AppCDS auf das experimentelle Kaninchen an. Da im Vergleich alles bekannt ist, benötigen wir einen Bezugspunkt - den Stand des Programms, mit dem wir die während des Experiments erzielten Ergebnisse vergleichen.


Einleitende Bemerkungen


  • Alle weiteren Befehle gelten für Linux. Unterschiede für Windows und MacOS sind nicht grundlegend.
  • Die JIT-Kompilierung kann die Ergebnisse spürbar beeinflussen und theoretisch aus -Xint der Reinheit des Experiments -Xint werden (mit der Option -Xint , wie im genannten Artikel beschrieben ). Aus Gründen der maximalen Glaubwürdigkeit wurde jedoch beschlossen, dies nicht zu tun.
  • Die folgenden Zahlen zur Startzeit wurden auf einem schnellen Testserver erhalten. Auf Arbeitsmaschinen sind ähnliche Zahlen in der Regel bescheidener, aber da wir nicht an absoluten Werten, sondern an prozentualen Inkrementen interessiert sind, halten wir diesen Unterschied für unbedeutend.
  • Um nicht vorzeitig auf die Komplexität der Messung des gemeinsam genutzten Speichers einzugehen, werden wir vorerst darauf verzichten, genaue Messwerte in Bytes zu erhalten. Stattdessen führen wir das Konzept des „ CDS-Potenzials “ ein, ausgedrückt als Prozentsatz der Anzahl gemeinsam genutzter Klassen zur Gesamtzahl der geladenen Klassen. Dies ist natürlich eine abstrakte Größe, wirkt sich jedoch direkt auf den tatsächlichen Speicherverbrauch aus. Darüber hinaus hängt seine Definition überhaupt nicht vom Betriebssystem ab, und für die Berechnung sind nur Protokolle ausreichend.

Bezugspunkt


Dieser Punkt sei der Zustand einer frisch heruntergeladenen Anwendung, d.h. ohne ausdrückliche Verwendung von AppCDS'ov und anderen. Um es zu bewerten, brauchen wir:


  1. Installieren Sie OpenJDK 13 (z. B. die inländische Liberica- Distribution, jedoch nicht die Lite-Version).
    Es muss beispielsweise auch der Umgebungsvariablen PATH oder JAVA_HOME hinzugefügt werden:


     export JAVA_HOME=~/tools/jdk-13 

  2. Laden Sie ANALOG herunter (zum Zeitpunkt des Schreibens war die neueste Version v0.12.1).


    Bei Bedarf können Sie in der Datei config/application.yaml im Parameter server.address den externen Hostnamen für den Zugriff auf die Anwendung angeben (standardmäßig wird dort localhost angegeben).


  3. Aktivieren Sie die Ladeprotokollierung für JVM-Klassen.
    Dazu können Sie die Umgebungsvariable JAVA_OPTS mit folgendem Wert JAVA_OPTS :


     export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log 

    Diese Option wird an die JVM übergeben und weist sie an , die Quelle jeder Klasse zu verpfänden .


  4. Führen Sie einen Testlauf durch:


    1. Führen Sie die Anwendung mit dem Skript bin/analog
    2. Öffnen Sie http: // localhost: 8083 im Browser, stecken Sie Schaltflächen und Morgengrauen
    3. Beenden Sie die Anwendung, indem Sie in der Konsole bin/analog script Ctrl+C drücken

  5. Nehmen Sie das Ergebnis (aus Dateien im log/ Verzeichnis)


    • Gesamtzahl der geladenen Klassen (nach class-load.log ):


       cat class-load.log | wc -l 10463 

    • Wie viele von ihnen werden aus einem freigegebenen Archiv heruntergeladen (entsprechend):


       grep -o 'source: shared' - class-load.log 1146 

    • Durchschnittliche Startzeit (nach einer Reihe von Starts; per analog.log ):


       grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225 



In diesem Schritt betrug das Potential von CDS also 1146/10463=0,1095 ~ 11% . Wenn Sie überrascht sind, woher die gemeinsam genutzten Klassen stammen (schließlich haben wir noch kein AppCDS aufgenommen), erinnere ich Sie daran, dass das JDK ab der 12. Version das fertige CDS-Archiv $JAVA_HOME/lib/server/classes.jsa von nicht weniger als fertig Liste der Klassen:


 cat $JAVA_HOME/lib/classlist | wc -l 1170 

Nachdem wir nun den Anfangszustand der Anwendung bewertet haben, können wir AppCDS darauf anwenden und im Vergleich verstehen, was dies ergibt.


Kernerfahrung


Wie die Dokumentation uns hinterlassen hat, müssen Sie zum Erstellen eines dynamischen AppCDS-Archivs nur einen Testlauf der Anwendung mit der Option -XX:ArchiveClassesAtExit . Ab dem nächsten Start kann das Archiv genutzt werden und Gewinn erzielen. Um dies an demselben experimentellen Kaninchen (AnaLog) zu überprüfen, benötigen Sie:


  1. Fügen Sie dem Befehl run die angegebene Option hinzu:


     export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa" 

  2. Protokollierung erweitern:


     export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log" 

    Diese Option erzwingt die Protokollierung des Prozesses zum Erstellen eines CDS-Archivs, wenn die Anwendung gestoppt wird.


  3. Führen Sie den gleichen Testlauf wie mit dem Referenzpunkt durch:


    1. Führen Sie die Anwendung mit dem Skript bin/analog
    2. Öffnen Sie http: // localhost: 8083 im Browser, stecken Sie Schaltflächen und Morgengrauen
    3. Beenden Sie die Anwendung, indem Sie in der Konsole bin/analog script Ctrl+C drücken
      Danach sollte ein riesiger Fußtuch mit allen möglichen Warnungen in die Konsole fallen und die log/cds.log sollte mit Details gefüllt sein. Sie interessieren uns noch nicht.

  4. Schalten Sie den Startmodus von "Test" auf "Nützlich" um:


     export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log" 

    Hier ergänzen wir die Variable JAVA_OPTS , sondern überschreiben sie mit neuen Werten, die (1) die Verwendung eines gemeinsam genutzten Archivs, (2) das Protokollieren von JAVA_OPTS und (3) das Protokollieren von Klassenpfadprüfungen umfassen.


  5. Führen Sie einen nützlichen Start der Anwendung gemäß dem Schema aus Absatz 3 durch.


  6. Nehmen Sie das Ergebnis (aus Dateien im log/ Verzeichnis)


    • Überprüfen, ob AppCDS wirklich angewendet wurde ( class-path.log ):


       [0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok 

      Die ok Markierungen nach den Zeilen type=BOOT und type=APP zeigen das erfolgreiche Öffnen, Überprüfen und Laden der integrierten bzw. angewendeten CDS-Archive an.


    • Gesamtzahl der geladenen Klassen (nach class-load.log ):


       cat class-load.log | wc -l 10403 

    • Wie viele von ihnen werden aus einem freigegebenen Archiv heruntergeladen (entsprechend):


       grep -o 'source: shared' -c class-load.log 6910 

    • Durchschnittliche Startzeit (nach einer Reihe von Starts; per analog.log Datei):


       grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167 



In diesem Schritt betrug das Potenzial von CDS jedoch bereits 6910/10403≈0,66 = 66% , 6910/10403≈0,66 es stieg um 55% gegenüber dem Referenzpunkt. Gleichzeitig wurde die durchschnittliche Startzeit um (4,5225-4,04167)=0,48 Sekunden verringert, d.h. Der Start ist um ~ 10,6% des Anfangswertes schneller.


Ergebnisanalyse


Der Arbeitstitel des Artikels lautet: "Warum so wenig?"


Wir haben alles nach den Anweisungen gemacht, aber nicht alle Klassen waren im Archiv. Ihre Anzahl beeinflusst die Startzeit nicht weniger als die Rechenleistung der Maschine des Experimentators, daher werden wir uns auf diese Zahl konzentrieren.


Wenn Sie sich erinnern, haben wir die Datei log/cds.log ignoriert, die beim Stoppen der experimentellen Anwendung nach dem Testlauf erstellt wurde. In dieser HotSpot-Datei hat die JVM freundlicherweise Warnklassen für jede Klasse notiert, die nicht im CDS-Archiv enthalten waren. Hier ist die Gesamtzahl solcher Marken:


 grep -o '[warning]' cds.log -c 3591 

Angesichts der Tatsache, dass im Protokoll class-load.log nur mehr als class-load.log Klassen erwähnt werden und 66% davon aus dem Archiv heruntergeladen werden, ist es nicht schwer zu verstehen, dass die in cds.log aufgeführten 3600 Klassen die „fehlenden“ 44% des CDS-Potenzials sind. Jetzt müssen Sie herausfinden, warum sie übersprungen wurden.


Wenn Sie sich das Protokoll cds.log ansehen, stellt sich heraus, dass es nur vier eindeutige Gründe für das Überspringen von Klassen gibt. Hier sind Beispiele für jeden von ihnen:


 Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded 

Unter allen 3591 verpassten Klassen finden sich hier folgende Gründe:



Schauen Sie sie sich genauer an:


  • Unsafe anonymous class
    JVM “” , -, .


  • Not linked
    , “” , , . , StackOverflow . , , “” () JAR- , AppCDS. , ( ).


  • Pre JDK 6 class
    , CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).


  • Skipping ... : super class/interface ... is excluded
    , “” . CDS', . Zum Beispiel:


     [warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded 


Fazit


CDS 100%.

, , , , , . .



JEP-310 , AppCDS JDK. . , . CDS (, , ) .


Um das experimentelle Kaninchen zu klonen (AnaLog in mehreren Fällen ausführen), müssen wir etwas an den Einstellungen ändern. Dadurch können die angehobenen Prozesse nicht „gebeugt“ werden. Dank Spring Boot können Sie dies tun, ohne Dateien zu bearbeiten oder zu kopieren. Alle Einstellungen können durch JVM-Optionen überschrieben werden. Das Weiterleiten dieser Optionen von einer Umgebungsvariablen ANALOG_OPTSstellt ein Startskript bereit, das freundlicherweise von Gradle generiert wird.


 export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091" 

JavaMelody, , , . TCP- ; .


, , JVM AppCDS . JAVA_OPTS JVM Unified Logging Framework :


 export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa" 

%p , JVM (PID). AppCDS , ( ).



, . . :


  1. server.port nodes.this.agentPort , :


     export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091" 

    , ( ).


  2. bin/analog


    () http://localhost:8091 ,


  3. PID ( ), :


     pgrep -f analog 13792 

  4. pmap ( ):


     pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB 

    ; .


  5. 1-4 (, ).




pmap . CDS' . , , PSS:


The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.

, , “ ” . , .


PSS , :


Iteration:12345
PSS of inst#1:339 088313 778305 517301 153298 604
PSS of inst#2:314 904306 567302 555299 919
PSS of inst#3:314 914311 008308 691
PSS of inst#4:306 563304 495
PSS of inst#5:294 686
Average:339 088314 341308 999305 320301 279

, - :


  • “”
  • , PSS
  • “” , PSS

, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa -Xshare:off , CDS . , .



:


  • PSS AppCDS CDS.
    . , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :


  • PSS AppCDS 2- ; 3- .
    , , , . , AppCDS, , , 3- .
    : , CDS? :


  • CDS/AppCDS JVM , PSS . , , pmap , “” sed '. :


     pmap -X `pgrep -f analog` 14981: # ... Address Perm Offset Device Inode Size Rss Pss ... Mapping # ... ... 7faf5e31a000 r-xp 00000000 08:03 269427 17944 14200 14200 ... libjvm.so # ... ... 7faf5f7f9000 r-xp 00000000 08:03 1447189 1948 1756 25 ... libc-2.27.so 

    ( Mapping ) , “” . JVM ( libjvm.so ), ( libc-2.27.so ). :


    For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so ) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.


. , , . , , JVM , Java- . GeekOut:



, , , AppCDS , .. Java-. , JVM, , - .


VisualVM Metaspace AppCDS , :


AppCDS



AppCDS



, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB ≈7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB ≈4,8 . , AppCDS , Metaspace. Metaspace, , CDS .


Anstelle einer Schlussfolgerung


Spring Boot AppCDS – JVM, .


  • JEP-350 Dynamic CDS Archives – JDK 13.
  • Spring Boot ó CDS ( ). , 100% - 66% . , ≈11% ( 15%, ).
  • , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
  • Metaspace, , AppCDS 5 , CDS.

, , AppCDS, , “killer feature”. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , …


by Nick Fewings on Unsplash

Source: https://habr.com/ru/post/de472638/


All Articles