Start / Seminare / Jakarta EE modernisieren

Modul

JVM oder Native Image

4 Kapitel in diesem Modul-Video · Laufzeit

Vertonung mit synthetischer Stimme · Inhalte redaktionell verantwortet · © 2026 HECKER CONSULTING, alle Rechte vorbehalten

Transkript

Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.

JVM oder Native Image

0:00 Kaum ein Thema wird in Quarkus-Vorträgen so prominent behandelt wie das native Abbild — und kaum eines wird in Projekten so oft aus den falschen Gründen gewählt. Deshalb der Leitsatz dieses Moduls: Nativ ist nicht besser. Nativ ist anders, und die Frage lautet, ob dieser Unterschied zu diesem Dienst passt. Wir sehen uns an, wie die native Übersetzung arbeitet, wo ihre Grenzen liegen und was sie wirklich kostet — und wir entwickeln einen Messaufbau, mit dem Sie die Entscheidung anhand Ihres eigenen Lastprofils treffen statt anhand fremder Erfahrungsberichte.

JVM oder Native Image

0:31 Wir sind im letzten Drittel. Der Dienst läuft, er ist getestet und beobachtbar — jetzt geht es um die Betriebsform. Und weil wir in Modul fünfzehn Telemetrie eingerichtet haben, können wir diese Entscheidung mit eigenen Zahlen treffen. Das ist der Unterschied zwischen einer Architekturentscheidung und einer Geschmacksfrage.

Wie Native Image arbeitet

0:51 Fangen wir mit dem Verfahren an. Denn wenn man versteht, was der Übersetzer tut, versteht man auch sofort, wo die Grenzen liegen. Denn wer versteht, was der Übersetzer tut, versteht auch sofort, wo seine Grenzen liegen — und warum sie sich erst zur Laufzeit zeigen. Bei der nativen Übersetzung analysiert GraalVM Native Image die Anwendung im Ganzen und erzeugt daraus ein eigenständiges Programm für eine bestimmte Plattform.

1:15 Der entscheidende Satz ist der letzte: Alles, was der Übersetzer nicht als erreichbar erkennt, ist im Ergebnis nicht enthalten. Stellen Sie sich vor, jemand packt Ihren Koffer nach der Liste dessen, was Sie nachweislich benutzt haben. Was auf der Liste fehlt, fehlt auch im Koffer — und Sie merken es am Zielort. Genau daraus ergeben sich alle Eigenheiten, über die wir gleich sprechen.

1:37 Diese Gegenüberstellung zeigt eine Verschiebung, kein Besser oder Schlechter. Bei der JVM findet die Übersetzung zur Laufzeit statt, und die Optimierung passiert im Betrieb — die JVM lernt, welche Pfade heiß sind, und wird mit der Zeit schneller. Nativ ist alles vorab entschieden: sehr schneller Start, aber keine Optimierung anhand des tatsächlichen Verhaltens.

1:58 Daraus folgt eine einfache Faustregel: Für kurzlebige Prozesse gewinnt nativ, für dauerhaft laufende Dienste unter Last spielt die JVM ihre Stärke aus. Welcher Fall Ihrer ist, entscheidet das Lastprofil. Worauf es hier ankommt, ist der mittlere Aufruf: Der Build läuft in einem Container, Sie brauchen also keine lokale GraalVM-Installation.

2:18 Das senkt die Einstiegshürde erheblich und macht den Bau auf jedem Rechner und in jeder Pipeline gleich. Und der dritte Aufruf ist, wie die Fußzeile sagt, der wichtigste: Er führt die Integrationstests gegen das erzeugte native Programm aus. Das ist kein optionaler Zusatz — es ist die einzige Stelle, an der die Eigenheiten der nativen Übersetzung überhaupt sichtbar werden.

2:39 Ohne ihn bauen Sie ein Artefakt, das Sie nicht geprüft haben. Vier Kosten, und die vierte zahlt das Team täglich: Jeder native Build in der Pipeline verlängert die Rückmeldung an die Entwicklung. Wenn ein Durchlauf statt vier Minuten zwanzig dauert, ändert das die Arbeitsweise — man schiebt weniger, man wartet länger, man schaltet Prüfungen ab.

2:59 Punkt zwei ist ein Infrastrukturthema, das gern übersehen wird: Der Speicherbedarf des Builds wächst mit der Anwendung, und Bauknoten sind oft knapp bemessen. Und Punkt drei ist eine harte Einschränkung: Das Ergebnis gilt nur für die Plattform, auf der es entstanden ist. Punkt eins ist der häufigste Pipelinefehler: Der native Build läuft bei jedem Commit.

3:19 Das ist selten nötig — nativ zu bauen genügt meist beim Release oder nachts, während der normale Durchlauf auf der JVM bleibt. Punkt zwei ist der Infrastrukturfehler mit unangenehmem Verlauf: Der Bauknoten hat zu wenig Speicher, und der Lauf bricht spät ab, nach zwanzig verlorenen Minuten. Punkt drei ist der Plattformfehler aus Punkt sieben.

3:39 Und Punkt vier ist der methodische: Die Bauzeit wird geschätzt statt einmal gemessen — dabei kostet die Messung genau einen Lauf.

Grenzen und Kompatibilität

3:47 Jetzt zu den Eigenheiten, die sich direkt aus dem Verfahren ergeben. Sie lassen sich in einem Satz zusammenfassen: Was der Übersetzer nicht sieht, gibt es später nicht. Sie lassen sich in einem Satz zusammenfassen: Was der Übersetzer nicht sieht, gibt es später nicht. Reflection, dynamisches Nachladen von Klassen, Proxys und Ressourcendateien sind für die Vorabanalyse nicht immer sichtbar.

4:11 Was gebraucht wird, muss deshalb angemeldet werden — über eine Annotation für eigene Klassen oder über Konfiguration für fremde Bibliotheken. Für migrierte Enterprise-Anwendungen ist das ein relevanter Punkt, denn gewachsener Code nutzt Reflection oft an Stellen, an die sich niemand mehr erinnert: bei der Serialisierung, in generischen Hilfsklassen, in selbstgebauten Frameworks.

4:32 Sie finden das nicht durch Lesen — Sie finden es, indem Sie das native Programm ausführen. Die Logik dieser Tabelle: Symptom, Fundstelle, Abhilfe. Und die Fußzeile ist der wichtigste Satz des Kapitels: Alle vier Fehlerbilder erscheinen erst beim Ausführen des nativen Programms, nie beim Build. Der Build meldet Erfolg. Das ist gewöhnungsbedürftig, denn wir sind es gewohnt, dass ein grüner Build eine Aussage über Vollständigkeit trifft. Hier trifft er sie nicht.

4:59 Die zweite Zeile ist die unangenehmste: Reflection tief in einer Fremdbibliothek, wo Sie weder die Stelle kennen noch den Code ändern können — dort hilft nur eine Konfigurationsdatei. Vier Punkte, und der erste ist die Begründung: Die üblichen Tests laufen auf der JVM und decken das native Verhalten schlicht nicht ab. Sie können hundert Prozent Abdeckung haben und trotzdem ein natives Artefakt ausliefern, das beim ersten Aufruf scheitert.

5:25 Punkt zwei ist die Konsequenz: Erst der Test gegen das gebaute Artefakt prüft, was tatsächlich ausgeliefert wird. Punkt drei ist ein praktischer Hinweis aus dem Alltag: Der Start des nativen Prozesses braucht im Test eine ausreichende Wartezeit, sonst gilt ein funktionierender Lauf als gescheitert — ein ärgerliches Missverständnis.

5:44 Punkt eins ist der Kernfehler: Es wird nativ gebaut, aber nur auf der JVM getestet. Damit haben Sie den Aufwand der nativen Übersetzung, aber keine ihrer Sicherheiten. Punkt zwei ist der typische Verlauf, wenn Punkt eins zutrifft: Eine Bibliothek nutzt Reflection, und der Fehler zeigt sich erst in der Abnahme — im schlechteren Fall in Produktion.

6:04 Punkt drei ist das Wartezeit-Missverständnis. Und Punkt vier nennt die Fundstellen, die fast jedes Projekt erwischen: Sprachdateien und Zertifikate fehlen im Abbild, weil sie als Ressourcen nicht aufgenommen wurden.

Belastbar entscheiden

6:17 Und jetzt die eigentliche Frage dieses Moduls. Sie lässt sich nicht allgemein beantworten — aber sie lässt sich messen. Sie lässt sich nicht allgemein beantworten — aber sie lässt sich messen, und zwar mit überschaubarem Aufwand. Die Entscheidung hängt am Lastprofil: Wie oft startet der Dienst, wie lange läuft er, wie viel Speicher kostet eine Instanz, und wie viel Durchsatz muss sie tragen?

6:39 Vier Fragen, die für Ihre Anwendung eine andere Antwort haben als für die aus dem Konferenzvortrag. Und der letzte Satz ist wichtig: Ohne eigene Messung ist die Entscheidung eine Übernahme fremder Erfahrung. Das mag gelegentlich gutgehen, aber es ist keine Entscheidung, die Sie in einem Architekturreview verteidigen können — und sie lässt sich später auch nicht überprüfen.

7:00 Die Logik dieser Tabelle steht in der dritten Spalte, den Gründen. Viele kurze Instanzen, etwa in einer stark schwankenden Umgebung — dort zählen Start und Speicher, und nativ gewinnt. Dauerhafte Last unter gleichbleibendem Verkehr — dort spielt die JVM ihre Optimierung im Betrieb aus. Und die letzte Zeile ist der ehrlichste Rat: Bei unklarem Profil die JVM, weil sie weniger Unbekannte im Betrieb hat.

7:24 Wie die Fußzeile sagt: Der JVM-Betrieb im Container ist eine vollwertige Zielform, kein Zwischenschritt. Man muss nicht nativ werden, um modernisiert zu sein. Der rote Faden: gleiches Ausgangsmaterial, gleiche Bedingungen, vollständige Kostenrechnung. Schritt zwei ist die methodische Voraussetzung — beide Varianten aus demselben Stand bauen, sonst vergleichen Sie zwei Anwendungen.

7:48 Schritt drei misst unter gleicher Last, nicht im Leerlauf. Und Schritt vier ist der, der in Vergleichen fast immer fehlt: die Bauzeit und ihre Wirkung auf die Pipeline. Wie die Fußzeile sagt, zahlt das Entwicklungsteam diese Kosten täglich — sie gehören also in dieselbe Rechnung wie die Speicherersparnis im Betrieb. Punkt eins ist der häufigste Messfehler: Gemessen wird die Startzeit, weil sie am leichtesten zu messen ist — und nicht der Durchsatz unter Last, der für einen dauerhaft laufenden Dienst viel wichtiger wäre.

8:19 Punkt zwei entwertet jede Messung: unterschiedliche Hardware. Punkt drei ist die unvollständige Rechnung, über die wir gesprochen haben. Und Punkt vier ist der ehrlichste auf dieser Liste: Nativ wird gewählt, weil es moderner klingt. Das ist ein legitimer Wunsch — nur sollte er nicht als technische Begründung auftreten, denn dann lässt er sich nicht überprüfen.

Übung

8:40 In der Übung messen Sie beide Varianten für Zählwerk. Und Sie werden sehen: Das Lastprofil gibt eine ziemlich klare Antwort. Überlegen Sie vorher, welche Variante Sie erwarten. Und prüfen Sie dann, ob die Messung Sie bestätigt. Sie bauen beide Varianten aus demselben Stand und messen Startzeit, Speicherbedarf, Durchsatz und Bauzeit.

9:00 Das Lernziel ist, zwei Betriebsformen unter gleichen Bedingungen zu vermessen und die Wahl anhand des eigenen Lastprofils zu begründen. Erfolgreich sind Sie, wenn alle vier Zahlen für beide Varianten vorliegen und die Entscheidung diejenige Zahl nennt, die den Ausschlag gab. Und der Hinweis nennt Ihnen das Profil: Zählwerk hat einen nächtlichen Ableselauf mit Spitzen am Monatsanfang und sonst geringe Dauerlast.

9:23 Überlegen Sie sich vorab, welche Variante Sie erwarten — und prüfen Sie dann, ob die Messung Sie bestätigt. Fünf Schritte, und Schritt zwei ist der, der die Übung interessant macht: Integrationstests gegen das native Artefakt. Wie die Fußzeile sagt — schlägt dieser Schritt fehl, ist der Befund wertvoller als jede Messung.

9:42 Denn dann haben Sie eine Reflection-Fundstelle entdeckt, und die kostet mehr Zeit als jeder Leistungsunterschied einbringt. Schritt fünf verlangt eine Zahl als Begründung, nicht eine Erzählung. Ein Satz genügt: Wir wählen die JVM, weil der Durchsatz unter Spitzenlast um so und so viel höher liegt und die Startzeit bei einem nächtlichen Lauf keine Rolle spielt.

10:03 Punkt eins ist der Fehler, der die ganze Messung wertlos macht: Die Varianten stammen aus verschiedenen Ständen. Punkt zwei ist die häufigste Verkürzung — messen ohne Last und damit am Einsatzzweck vorbei. Punkt drei ist der unehrliche: Der native Lauf scheitert an Reflection, und die Messung wird trotzdem berichtet, weil die Startzeit ja beeindruckend war.

10:22 Und Punkt vier ist der, gegen den dieses ganze Modul geschrieben ist: Die Entscheidung fällt vor der Messung und sucht sich anschließend die Zahlen, die zu ihr passen. Messen Sie zuerst.

Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →