6. Februar 2024 von Merlin Bögershausen
Hidden Heros in Java 21
Die Sprache Java und das JDK-Ökosystem sind voller verborgener Schätze. Diese zu entdecken erfordert eine kleine Expedition durch einige JDK Enhancement Proposals, JEPs genannt. In diesem Blog-Artikel begeben wir uns auf eine Expedition und entdecken Code-Snippets in JavaDoc sowie den beschleunigten Start einer Anwendung mit AppCDS.
Java Snippets in JavaDoc
In den meisten Fällen werden Programme entwickelt, die über Jahre gepflegt und gewartet werden müssen. Solche Programme benötigen eine solide Dokumentation, insbesondere wenn sie in anderen Kontexten wiederverwendet werden sollen. Aus diesem Grund wurde bereits in der ersten Version des JDK das Tool javadoc eingeführt. Mit javadoc können Kommentare im Quellcode in eine durchsuchbare HTML-Schnittstellen-Dokumentation umgewandelt werden.
In solchen technischen Dokumenten ist es sinnvoll, die beabsichtigte Verwendung der Schnittstelle zu skizzieren. Zu diesem Zweck wird oft der @code-Tag verwendet, der den Source Code hervorhebt. Leider ist das @code-Tag nicht gut für mehrzeilige Codepassagen geeignet. Dies hat sich seit Java 18 mit dem neuen @snippet-Tag geändert. Im folgenden Listing wird die Verwendung des @snippet-Tags für die Klasse MwStRechner demonstriert. In der fertigen Dokumentation bleibt die Formatierung erhalten, nur mehrzeilige Kommentare sind nicht erlaubt.
/**
* Berechnung der MwSt für einen Privatkunden beim Einkauf in Höhe von 1055
* {@snippet :
* var kunde = new Privatkunde("Merlin", "");
* var wert = 1055d;
* // ...
* var mwst = MwStRechner.PlainOOP.calculateMwSt(kunde, wert);
* }
*/
Leider kann der Code nicht getestet werden und kann Syntaxfehler enthalten. Um dieses Problem zu lösen, können Teile von Java-Dateien mit dem @snippet-Tag eingebunden werden. Innerhalb der zugrundeliegenden Dateien können Transformationen definiert werden, um die Lesbarkeit und Aussagekraft zu verbessern. Dazu gehören unter anderem:
- Ersetzen von Text durch anderen Text
- Hervorhebungen durch Regex-Ausdrücke
- Definitionen von Bereichen
Eine vollständige Liste findet sich in JEP 412 [1]. Das folgende Listing zeigt ein vollständiges Beispiel, wie ein Bereich aus der Datei snippet-files/SwitchExpressionsSnippets.java in das JavaDoc einer anderen Klasse eingebunden werden kann.
/ Datei: MwStRechner.java
/**
* Berechnung der MwSt für einen Privatkunden beim Einkauf in Höhe von 1055
* {@snippet file="SwitchExpressionsSnippets.java" region="example"}
*/
// Datei: snippet-files/SwitchExpressionsSnippets.java
class Snippets {
void snippet01() {
// @start region="example"
// @replace region="replace" regex='(= new.*;)|(= [0-9]*d;)' replacement="= ..."
// @highlight region="highlight" regex="\bMwStRechner\b"
var kunde = new Privatkunde("Merlin", "test@other.de"); // @replace regex="var" replacement="Kunde"
var wert = 1055d; // @replace regex="var" replacement="double"
/* .. */
var mwst = MwStRechner.PlainOOP.calculateMwSt(kunde, wert); // @link substring="PlainOOP.calculateMwSt" target="MwStRechner.PlainOOP#calculateMwSt"
// @end @end @end
}
}
In diesem Beispiel habe ich Bereiche ersetzt und markiert. Das Endergebnis gibt einen guten Überblick über die Verwendung und kann regelmäßig getestet werden. Mit diesem weniger bekannten, aber mächtigen Werkzeug des Java-Ökosystems lässt sich die klar strukturierte Schnittstellendokumentation sehr einfach mit Beispielen anreichern.
Dynamic App Class Data Sharing
Die Java Virtual Machine (JVM) wurde zu einer Zeit entwickelt, in der die Spitzenleistung von lang laufenden Anwendungen viel relevanter war als in der heutigen Microservice-getriebenen Zeit. Einige der damals besten Heuristiken wirken sich heute negativ auf die Performance von Microservices aus. Sie sind der Grund für die vergleichsweise schlechte Startperformance der JVM.
Um die Startperformance einer JVM-Anwendung zu verbessern, ist es notwendig, den initialen Classloading-Prozess zu beschleunigen. In diesem Schritt des JVM-Starts werden Informationen über die verwendeten Klassen geladen und analysiert. Um die Wiederholung dieses Schrittes zu vermeiden, wurde die JVM um das Prinzip des Class Data Sharing (CDS) erweitert. Bei CDS werden die gesammelten Informationen aus dem Classloading in einem CDS-Archiv persistiert und können zu einem späteren Zeitpunkt wieder verwendet werden.
Die JVM enthält bereits ein Archiv mit der Java-Standardbibliothek. Das Archiv befindet sich als `classes.jsa` Datei im Teil der ausgelieferten JVM und wurde mit einer für G1 konfigurierten JVM mit 128MB Heapspace erstellt. Falls eine andere Heap-Konfiguration oder weitere Klassen benötigt werden, muss ein eigenes Archiv erstellt werden. Mit Application Class-Data Sharing, auch AppCDS genannt, kann auch der Classpath einer Anwendung in die erzeugten CDS-Archive übernommen werden. Dadurch kann die Startzeit der JVM erheblich verkürzt werden. Wenn ihr die gleiche Anwendung auf mehreren JVMs betreibt oder häufig neu startet, erhöht sich die Ersparnis enorm.
Um ein CDS-Archiv zu erstellen, benötigt man Informationen über die Klassen, die zur Laufzeit in einer Anwendung verwendet werden. Diese Informationen können von der JVM selbst ermittelt werden:
- Ein Trainingslauf, bei dem die Anwendung mit zusätzlichen Parametern gestartet wird.
- 1. Mit -Xshare:off -XX:DumpLoadedClassList=clazzes.cls wird die Applikation gestartet und eine Liste der geladenen Klassen als Datei clazzes.cls erzeugt.
- 2. mit -Xshare:dump -XX:SharedArchiveFile=MyApp.jsa -XX:SharedClassListFile=clazzes.cls -cp app.jar wird ein CDS-Archiv der Applikation in der Datei MyApp.jsa erzeugt.
- Per JCMD kann ein CDS-Archiv in Produktion bestimmt werden, dazu muss.
- 1. muss die Applikation mit dem Parameter -XX:+RecordDynamicDumpInfo gestartet werden.
- 2. Der Befehl jcmd <pid> VM.cds dynamic_dump my_dynamic_archive.jsa d erzeugt ein CDS-Archiv VM.cds für die angegebene PID.
- Die JVM kann automatisch nach dem Beenden ein CDS-Archiv erzeugen. Dazu wird beim Start der Parameter -XX:ArchiveClassesAtExit=MyApp.jsa angegeben.
Um eine Anwendung mit dem generierten CDS-Archiv zu starten und von den gesammelten Informationen zu profitieren, muss die JVM mit dem Parameter -XX:SharedArchiveFile=MyApp.jsa gestartet werden. Mit diesen wenigen zusätzlichen Schritten lässt sich die Startleistung einer JVM-Anwendung deutlich verbessern. Gleichzeitig kann man früher von der hohen Peak-Performance profitieren. Mit diesen Anpassungen ist die JVM auch für Umgebungen gewappnet, in denen die JVM neu gestartet werden muss.
Zusammenfassung
In diesem Blog-Beitrag habe ich zwei weniger bekannte Werkzeuge aus dem Java-Ökosystem vorgestellt. Mit Hilfe dieser Werkzeuge lassen sich schnell startende und ressourcenschonende Anwendungen mit eleganten und gut dokumentierten Schnittstellen entwickeln. Unter anderem zu diesem Thema halte ich Vorträge auf Konferenzen und schreibe weitere Beiträge adesso Blog. Bei Fragen und Problemen stehen ich oder unsere Expertinnen und Experten von adesso gerne zur Verfügung.
Ihr möchtet mehr über spannende Themen aus der adesso-Welt erfahren? Dann werft auch einen Blick in unsere bisher erschienenen Blog-Beiträge.