Energieeffizienz durch JVM-Auswahl: Relevanz der richtigen Distribution
Von Julius Liebau
Java spielt seit der Einführung Mitte der 1990er Jahre eine bedeutende Rolle in der Softwareentwicklung. Somit bringt sie ein reiches technisches Erbe mit. Auch im Jahr 2024 gehört Java laut dem TIOBE-Index noch zu den Top 3 der beliebtesten Programmiersprachen. Laut aktuellen Schätzungen verwenden über 90 Prozent der Fortune-500-Unternehmen Java für ihre Anwendungen (Unimedia Tech 2024). Java-basierte Anwendungen sind in der heutigen Unternehmenslandschaft somit allgegenwärtig.
Ein maßgeblicher Faktor für den Erfolg von Java ist die Plattformunabhängigkeit. Diese wird durch die Nutzung einer Laufzeitumgebung ermöglicht. Die Laufzeitumgebung wird als Java Virtual Machine (JVM) bezeichnet und fungiert dabei als Abstraktionsschicht zwischen dem Java-Bytecode und dem zugrunde liegenden Betriebssystem und der Hardware. JVMs bilden somit das Fundament für die Ausführung von Java und anderen JVM-basierten Sprachen wie Kotlin, Scala und Groovy.
Die Popularität und praktische Relevanz von Java bieten einen idealen Anlass, den Energieverbrauch und die Optimierungsmöglichkeiten Java-basierter Anwendungen genauer zu untersuchen. Denn interessant ist: Ein und dieselbe Java-Anwendung kann je nach Konfiguration der Laufzeitumgebung mehr oder weniger energieeffizient betrieben werden.
In dieser Beitragsreihe wollen wir untersuchen, welche verschiedenen Ansätze es gibt, um die Laufzeit von Java-Anwendungen energieeffizienter zu gestalten und wo sich Optimierungspotentiale in der Konfiguration von JVMs verstecken.
Das Ziel ist die Reduktion des Ressourcenverbrauchs (z.B. CPU-Auslastung), um so die Hardwareanforderungen und die damit verbundenen Kosten für die Umwelt und für das Geschäft zu senken. Für den Nutzenden ergeben sich u.a. schnellere Reaktionszeiten.
In diesem ersten Blog-Beitrag der Serie zur Optimierung von Java-Anwendungen konzentrieren wir uns auf die Auswahl der Java Virtual Machine (JVM)-Distribution. Diese grundlegende Entscheidung kann bereits erheblichen Einfluss auf die Leistung und Effizienz von Java-Anwendungen haben.
Funktionsweise der JVM
Portabilität bedeutet, dass Java-Anwendungen auf allen Betriebssystemen und Hardware-Plattformen gleich funktionieren. Erreicht wird das, indem der Source Code durch den Java Compiler nicht direkt in Maschinencode übersetzt wird, sondern in den sogenannten Java-Bytecode, der dann von einer Java Virtual Machine (JVM) ausgeführt wird. Der Code läuft somit nicht direkt auf der Hardware (Lenovo 2024). Das führt dazu, dass Java-Programme zunächst langsamer laufen und mehr Speicher benötigen als solche in C++. Die in die JVM integrierte Just-in-Time- (JIT-)Kompilierung steigert jedoch die Leistung, indem sie zur Laufzeit gesammelte Profilinginformationen nutzt, um den Bytecode in Maschinencode umwandelt. Darüber hinaus nutzt Java einen automatischen Garbage Collector (GC), um den Speicher während der Lebensdauer von Objekten zu verwalten und nicht mehr benötigten Speicher freizugeben (Mohammed Chakib Belgaid 2022).
Ein strategischer Ansatz: Die Auswahl der JVM-Distribution
Die Wahl der JVM-Distribution kann einen signifikanten Einfluss auf den Energieverbrauch einer Java-Anwendung haben. JVM-Implementierungen unterscheiden sich in ihren Optimierungsstrategien, Garbage-Collection-Algorithmen und Just-in-Time-Compilern, was sich direkt auf die Effizienz und den Ressourcenverbrauch auswirkt.
Es gibt zahlreiche JVM-Distributionen. In einer umfangreichen Analyse von Mohammed Chakib Belgaid mit verschiedenen Benchmarks und Versionen der JVMs hat sich gezeigt, dass sich die JVM-Distributionen in drei repräsentative Gruppen bezüglich des Energieverbrauchs einteilen lassen: J9, HotSpot und seine Varianten sowie GraalVM. Die einzelnen Ergebnisse möchten wir uns im Folgenden genauer anschauen.
HotSpot: Die ausgewogene Standard-JVM für vielseitige Anwendungen
HotSpot ist die Standard-JVM, die mit den meisten Java-Installationen ausgeliefert wird. Sie ist demnach weit verbreitet. Die Analyse von Belgaid zeigt, dass sich diese JVM gut für allgemeine Anwendungsfälle eignet, da sie eine ausgewogene Mischung aus Leistung und Energieeffizienz bietet und in den neuesten Versionen deutliche Verbesserungen in puncto Energieeinsparungen zeigt. Experimentelle Messungen haben gezeigt, dass HotSpot in verschiedenen Benchmarks solide Energieeffizienz-Werte erzielt und speziell ab der Version 15 gegenüber älteren Versionen wie HotSpot-8 einen signifikant geringeren Energieverbrauch aufweist (ebd.).
Bei langlaufenden Server-Anwendungen, bei denen Stabilität und Durchsatz im Vordergrund stehen – beispielsweise in Webanwendungen oder Unternehmenssoftware -, kann HotSpot eine gute Wahl sein. Nach einer Warm-up Phase führt die Aktivierung der JIT-Kompilierung zu Energieeinsparungen (ebd.).
GraalVM: Hochleistung und Energieeffizienz für spezialisierte Anwendungen
GraalVM ist für seine Schnelligkeit und Ressourcensparsamkeit bekannt und hat sich auch in Tests als eine der energieeffizientesten JVM-Distributionen behaupten können. Diese JVM zeigt vor allem bei kurzen und intensiven Workloads, wie sie typischerweise in Microservices-Architekturen oder bei rechenintensiven Aufgaben vorkommen, eine sehr gute Energieeffizienz.
Dank der optimierten Laufzeitumgebung und der Unterstützung von „Ahead-of-Time“ (AOT)-Kompilierung, kann GraalVM dabei helfen, Energie zu sparen und somit die CO₂-Emissionen zu verringern (Ha Do Long et. al 2023). GraalVM liefert insgesamt gute Ergebnisse und gehört zu den Distributionen mit niedrigem Energieverbrauch für fast alle Benchmarks. Im Vergleich zu HotSpot und J9 schneidet die GraalVM nur unter einer von zwölf Benchmarks am schlechtesten ab (Mohammed Chakib Belgaid 2022).
Bei der AOT-Kompilierung (auch native Kompilierung genannt) wird der Programmcode vor der Ausführung in Maschinensprache übersetzt. Das kann die Startzeit einer JVM-basierten Anwendung erheblich verkürzen. Im Gegensatz zur Just-in-Time (JIT)-Kompilierung gibt es bei einer AOT-Kompilierung jedoch keine Möglichkeit zur dynamischen Laufzeitoptimierung. JIT-Compiler wie in HotSpot optimieren den Code fortlaufend zur Laufzeit, was langfristig zu einem höheren Durchsatz führen kann. Der Grund dafür ist, dass die JVM kontinuierlich Anpassungen basierend auf dem tatsächlichen Verhalten der Anwendung vornimmt (Red Hat 2022).
Die GraalVM bietet Flexibilität für verschiedene Anwendungsszenarien. Für serverlose Architekturen oder Anwendungen mit kurzen, rechenintensiven Aufgaben eignet sich die native Kompilierung. Sie bietet Vorteile bei Startzeit und initialem Ressourcenverbrauch, was zu einer verbesserten Energieeffizienz führt. Die JIT-Kompilierung mit GraalVM kann für länger laufende Prozesse (typischerweise in Microservice-Architekturen) mit variablen Lastprofilen von Vorteil sein, da sie kontinuierliche Optimierungen ermöglicht (Štefanko and Jan Martiška, Quarkus in Action).
J9: Energieeffizient bei spezifischen Workloads
IBM’s J9 (auch als Eclipse OpenJ9 bekannt) stellt eine weitere Option dar.
J9 hat einen erhöhten Energieverbrauch bei Benchmarks gezeigt, die eine kontinuierliche und längere Verarbeitung benötigen. Allerdings bietet es gute Einsparmöglichkeiten bei bestimmten Benchmarks wie Avrora, einem für Embedded-Anwendungen typischen Workload. Hier verbrauchte J9 sogar bis zu 38 Prozent weniger Energie als HotSpot (Mohammed Chakib Belgaid 2022). Anwendungen, die kontinuierlich, aber mit geringerer Last laufen, profitieren somit möglicherweise von J9.
J9 kann für Anwendungen in Betracht gezogen werden, bei denen geringe Spitzenlasten und eine moderate, konstante Auslastung erwartet werden, wie etwa bei Datenbanksystemen. Auch in Szenarien mit hoher Parallelität und Asynchronität (Benchmark Reactors) kann die J9 energieeffizient sein (ebd.). Wenn hohe Spitzenleistungen gefordert sind, sind GraalVM oder HotSpot tendenziell eine bessere Wahl.
JReferral: Eine Entscheidungshilfe
JReferral ist ein leicht zu bedienendes Open-Source-Tool, dass Java-Programme durch verschiedene Versionen von JVMs und Optimierungsoptionen ausführt, um die energieeffizienteste Konfiguration zu empfehlen.
Um JReferral nutzen zu können, wird ein Linux-System, Docker und mindestens Python 3.5 benötigt. Das Tool setzt auf die RAPL-Sensoren (Running Average Power Limit) des Prozessors, um den Energieverbrauch während der Ausführung zu messen. Docker dient dabei der Isolierung der JVM-Instanzen, um möglichst präzise Ergebnisse zu erhalten.
Die Funktionsweise von JReferral ist leicht verständlich: Zunächst wird in der Konfigurationsdatei jvms.sh
festgelegt, welche JVM-Versionen und Optimierungsoptionen getestet werden sollen. Danach erstellt das Tool über das Skript build-images.sh
Docker-Container für jede ausgewählte JVM. Anschließend wird der übliche Java-Ausführungsbefehl „java“ durch ./jreferral.sh
ersetzt. Die Ergebnisse werden als CSV-Datei gespeichert, während ein zusätzliches Log die Testdurchführung dokumentiert.
Es besteht auch die Möglichkeit, mehrere Benchmarks gleichzeitig zu testen, was gerade für komplexere Projekte nützlich ist. Der Energieverbrauch von CPU und RAM lassen sich in einem PDF-Bericht visualisieren und so können Optimierungsentscheidungen datengestützt getroffen werden.
Fazit
Die Wahl der richtigen JVM-Distribution hängt stark vom Anwendungskontext ab. Das Ziel dieses Artikels ist es, ein Bewusstsein für den Einfluss der JVM-Distribution auf den Energieverbrauch zu schaffen. Die hier präsentierten Erkenntnisse bieten eine Orientierung, um die Energieeffizienz der Anwendungen durch die Auswahl der JVM zu steigern. Sie beruhen auf empirischen Untersuchungen ausgewählter JVM-Versionen und Benchmarks, die richtungsweisende Hinweise geben, jedoch nicht verallgemeinert werden können. Die Auswahl der optimalen JVM erfordert immer eine angepasste Analyse, bei der die spezifischen Anforderungen und das Verhalten der jeweiligen Anwendung im Blick behalten werden müssen. Um die Energieeffizienz von Java-Anwendungen zu verbessern, lohnt sich ein genauerer Blick auf die JVM-Distribution allemal.