GEDOPLAN
JavaQuarkus

Quarkus Native – Free Performance für die Cloud?

JavaQuarkus
quarkus native 1

Bei modernen Java-Anwendungen (insbesondere in der Cloud) stößt man früher oder später unweigerlich auf native Java-Anwendungen bzw. Quarkus Native. Das Versprechen sind kleine Container, minimaler RAM-Verbrauch und Startzeiten im Millisekundenbereich. Doch wie so oft gibt es auch hier einen Trade-Off aus Vor- und Nachteilen zu bedenken, die wir uns hier mal etwas genauer ansehen. Dabei nehmen wir einmal Bezug auf ein kleines Demoprojekt, aber auch auf offizielle Quarkus Benchmarks.

Was bedeutet „Native“ und wie funktioniert’s?

quarkus native

Traditionell funktioniert Java nach dem Prinzip „Write Once, Run Anywhere“. Der Quellcode wird in plattformunabhängigen Bytecode übersetzt, den die Java Virtual Machine (JVM) erst zur Laufzeit liest, optimiert und per Just-In-Time-Compiler (JIT) in Maschinencode übersetzt. Auch Quarkus-Anwendungen basieren in der Regel auf dieser Grundlage.

Native Java-Anwendungen brechen mit diesem Prinzip und der Code wird durch einen Ahead-of-Time-Compiler (AOT) bereits zur Build-Phase komplett in eine plattformspezifische und nativ ausführbare Binärdatei übersetzt. Quarkus Native hilft uns dabei Quarkus-Anwendungen nativ zu betreiben.

Der Motor hinter Quarkus Native ist GraalVM (bzw. Mandrel, eine von Red Hat optimierte GraalVM-Distribution speziell für Quarkus). Bei der nativen Komplierung verwendet Quarkus Native eine „Closed-World-Assumption“. Es scannt den gesamten Code, vom Einstiegspunkt ausgehend, und wirft alles weg, was nachweislich nicht erreichbar ist. Das Ergebnis wird dann, mit einer minimalen Laufzeitumgebung, in ein einzelnes Artefakt verpackt.

Startzeiten und Ressourcenverbrauch zur Laufzeit

Wenn wir das Artefakt (z.B. in einem Container) starten fällt zunächst die extrem kurze Startzeit positiv auf. Statt mehreren 1000 ms (JVM) startet die Anwendung in weniger als 100 ms. Auch der Einfluss auf die Arbeitsspeichernutzung scheint groß. Die Nutzung halbiert sich bei unserem kleinen Demoprojekt, im Quarkus Benchmark sinkt sie von 269 MiB auf 77 MiB.

Zeit und Ressourcenverbrauch zur Buildzeit

Wir haben bereits am Anfang des Beitrags den AOT-Compiler, der den Quellcode analysiert und in eine ausführbare Binärdatei übersetzt, erwähnt. Aktivieren können wir den nativen Build, indem wir das von Quarkus angelegte Maven-Profil native aktivieren. Dazu bedarf es einer lokalen GraalVM Installation, optional ist aber auch ein Build im Container möglich. Die extrem kurzen Startzeiten und optimierte Arbeitsspeichernutzung erkauft man sich durch eine deutlich längere und ressourcenintensive Buildzeit. Unser kleines Beispielprojekt baut im nicht ganz repräsentativen Test statt 23 Sekunden mehr als 3,5 Minuten. Die CPU wird in dieser Zeit maximal ausgelastet und statt 550 MB verbraucht der Build über 3,5 GB RAM. Angenommen ein CI/CD-System baut mehrere Anwendungen gleichzeitig, können die Ressourcen schnell knapp werden.

Mögliche Probleme zur Laufzeit

Da GraalVM durch die „Closed-World-Assumption“ bereits zur Buildzeit wissen muss was zur Laufzeit passiert, gibt es einige Stellen an denen zur Laufzeit Probleme auftreten können. Insbesondere das Einbinden von Ressourcen und die Nutzung von Reflection (möglicherweise auch implizit durch Bibliotheken) kann hier Schwierigkeiten machen. Verwendete Ressourcen müssen möglicherweise explizit über Properties eingebunden werden und Klassen, die erst zur Laufzeit initialisiert werden sollen, dafür erst aktiviert werden (vgl. src/main/resources/application.properties). Klassen, die der Compiler übersieht, weil sie nicht direkt verwendet werden, müssen eventuell erst per Annotation aktiviert werden (vgl. src/main/java/de/gedoplan/ekj/domain/Planet.java). Diese Probleme sind im JVM-Modus (z.B. im Quarkus Dev-Mode) zunächst nicht unbedingt abzusehen und können nur durch Tests der nativen Anwendung reproduziert werden. Nach einer möglichen Fehlerbehebung muss zunächst der ressourcenintensive Build-Prozess abgewartet werden, bevor der Test wiederholt werden kann. Debugging und Problemlösungen werden so erschwert.

Auch in eingesetzten Bibliotheken und Abhängigkeiten können Probleme durch die native Kompilierung auftreten. Hier ist es ratsam auf eine entsprechende Quarkus-Extension für die jeweilige Bibliothek zurückzugreifen, wodurch einige Probleme bereits behoben werden.

Zusätzlich sehen wir im Quarkus Benchmark, dass die JVM-Variante nach einem deutlich langsameren Start trotzdem einen höheren Durchsatz erreichen kann. Performancevorteile sind also primär bei Anwendungen ohne lange Laufzeiten und häufigen Starts zu erwarten. Ein Szenario was bei automatischer Skalierung in einer Kubernetes Umgebung allerdings durchaus vorkommen kann.

Welche Anwendungsfälle bleiben?

Der Einsatz von Quarkus Native bleibt also eine Entscheidung die nicht leichtfertig getroffen werden sollte. Insbesondere für kleinere Anwendungen/Microservices mit geringen Abhängigkeiten, die in einem Kubernetes Kontext in wenigen Millisekunden bereitgestellt werden sollen ist der Startzeitvorteil unschlagbar. Auch wenn RAM und CPU Ressourcen zur Laufzeit extrem knapp sind kann es sich lohnen einen Blick auf Quarkus Native zu werfen. Bei klassischeren Anwendungen und Services, die (möglicherweise trotzdem in einem Kubernetes) wochenlang durchlaufen ist der JVM-Modus häufig weiter die bessere und stressfreiere Wahl. Da Quarkus auch im JVM-Modus einige (nicht so problematische) Vorbereitungen zur Buildzeit durchführt, bleiben die Startzeiten hier trotzdem im Rahmen.

Wer dennoch auf einen weiteren Startzeitvorteil pocht sollte sich die Entwicklungen zum AOT-Caching in den aktuellen Java Versionen ansehen. Hier kann nach dem Build durch einen Trainingslauf ein AOT-Cache aufgebaut werden, der zur Laufzeit für einen schnelleren Anwendungsstart im JVM-Modus verwendet werden kann. Genaueres findet ihr in dem Blog-Beitrag zum Thema.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

IT-Training - GEDOPLAN
Entwicklungswerkzeuge

Upgrade auf Maven 3

Wir benutzen für unsere Projekte schon seit Langem Maven als Build-Werkzeug. Nachdem die neue Version 3 schon seit ein paar…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!