Java ist bekannt für seine Plattformunabhängigkeit: Einmal kompilierter Bytecode läuft auf jeder JVM. Doch dieser Komfort hat seinen Preis – die JVM muss zur Laufzeit starten, Klassen laden, Bytecode interpretieren und zur Laufzeit optimieren (JIT). Für Microservices, Serverless Functions oder CLI-Tools kann dieser Overhead störend sein, besonders in Container-Umgebungen, wo kurze Startzeiten und geringer Speicherverbrauch zählen. Hier kommen GraalVM Native Images ins Spiel.

Was ist GraalVM?

GraalVM ist eine High-Performance-Laufzeitumgebung, die über eine klassische JVM hinausgeht. Kernstück ist der Ahead-of-Time (AOT) Compiler, der Java-Bytecode direkt in nativen Maschinencode übersetzt – noch bevor das Programm ausgeführt wird. Das Ergebnis ist eine standalone, ausführbare Binary, die komplett ohne JVM auskommt.

GraalVM wurde ursprünglich von Oracle Labs entwickelt und ist mittlerweile in zwei Editionen verfügbar: die Community Edition (CE) als Open Source und die Enterprise Edition (EE) mit zusätzlichen Optimierungen.

Vorteile von Native Images

  • Schneller Start: Ein Spring Boot REST-Service startet in Millisekunden statt 3–5 Sekunden auf der JVM
  • Geringer Speicherverbrauch: Native Images benötigen oft nur 10–30 MB RAM, während die äquivalente JVM-Anwendung 100–300 MB belegt
  • Keine JVM-Abhängigkeit: Die Binary lässt sich ohne installiertes JDK ausführen – ideal für minimale Docker-Images (~10 MB statt 200+ MB)
  • Sofortige Spitzenleistung: Keine JIT-Compilation-Warm-Up-Phase; das native Image läuft ab Start auf seinem maximalen Performance-Niveau – zu beachten ist, dass JIT-optimierter JVM-Code bei länger laufenden Prozessen durch Profiling-basierte Optimierungen höhere Durchsatzraten erreichen kann

Nachteile und Einschränkungen

  • Längere Build-Zeit: Der AOT-Compiler muss das gesamte Programm inklusive aller Abhängigkeiten analysieren (oft 1–5 Minuten)
  • Closed-World-Annahme: Reflection, dynamische Klassenladung (Class.forName()), Serialisierung und dynamische Proxys sind nur eingeschränkt nutzbar
  • Kein dynamisches Classloading: Frameworks, die CGLIB oder CDI-Proxys nutzen, benötigen explizite Konfiguration
  • Eingeschränkte Debugging-Möglichkeiten: Stacktraces sind weniger aussagekräftig als auf der JVM

Erste Schritte mit Native Images

Zuerst muss GraalVM installiert sein. Mit SDKMan geht das am einfachsten:

sdk install java 24.0.1-graal
Code-Sprache: CSS (css)

Danach installiert man das native-image Tool:

gu install native-image

Eine einfache Java-Klasse lässt sich wie folgt nativ kompilieren:

public class HalloWelt {
    public static void main(String[] args) {
        System.out.println("Hallo aus einem nativen Image!");
    }
}
Code-Sprache: JavaScript (javascript)

Kompilieren und ausführen:

javac HalloWelt.java
native-image HalloWelt
./hallowelt

Die Ausgabe erscheint nahezu sofort – messbar im zweistelligen Millisekundenbereich. Ein vergleichbarer java HalloWelt-Aufruf braucht auf der JVM inklusive JVM-Start 200–500 ms.

Native Images mit Spring Boot

Spring Boot 4.x unterstützt GraalVM Native Images nativ. Die integrierte AOT-Engine von Spring Boot generiert während der Build-Phase die notwendige Konfiguration für Reflection, Ressourcen und Proxys automatisch.

Maven-Konfiguration

<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>0.11.0</version>
</plugin>
Code-Sprache: HTML, XML (xml)

Bauen des nativen Images:

mvn -Pnative native:compile
Code-Sprache: CSS (css)

Die resultierende Binary liegt im target/-Verzeichnis. Ein typischer Spring Boot REST-Controller startet damit in unter 0,1 Sekunden – ein beeindruckender Unterschied zu den 3–5 Sekunden auf der klassischen JVM.

Docker-Image mit Native Image

Ein Dockerfile für das native Image fällt minimal aus:

FROM debian:bookworm-slim
COPY target/meine-anwendung /app
ENTRYPOINT ["/app"]
Code-Sprache: JavaScript (javascript)

Das resultierende Docker-Image wiegt oft unter 20 MB, während ein herkömmliches Eclipse-Temurin-Image bei über 200 MB liegt.

Reflection-Konfiguration

Da GraalVM eine Closed-World-Annahme trifft, müssen Reflection-Zugriffe explizit deklariert werden. Spring Boot generiert diese Konfiguration automatisch während der AOT-Phase. Bei manuellen Projekten oder wenn Bibliotheken dynamisch auf Klassen zugreifen, erstellt man eine META-INF/native-image/reflect-config.json im Ressourcenverzeichnis:

[
  {
    "name": "com.example.MyService",
    "methods": [
      { "name": "findAll", "parameterTypes": [] }
    ]
  }
]
Code-Sprache: JSON / JSON mit Kommentaren (json)

Weitere Konfigurationsdateien sind resource-config.json (für Ressourcen wie .properties oder Templates) und proxy-config.json (für dynamische Proxys). Viele moderne Bibliotheken wie Jackson, Hibernate oder Spring Boot liefern mittlerweile automatisch generierte Konfiguration aus.

Performance-Vergleich: JVM vs. Native Image

KriteriumKlassische JVMNative Image
Startzeit (Hello World)~300 ms~5 ms
Startzeit (Spring Boot)~4 s~80 ms
Speicher (Spring Boot)~150 MB~25 MB
Docker-Image-Größe~250 MB~15 MB
Build-Zeit~10 s~2–3 min
Reflectionuneingeschränktkonfigurationspflichtig

Insbesondere für kurze Prozesse wie Serverless Functions oder CLI-Tools, die nur wenige Sekunden laufen, sind die Startzeit- und Speichervorteile entscheidend. Die JVM würde hier die meiste Zeit mit dem Start verbringen, während das native Image sofort produktiv arbeitet.

Fazit

GraalVM Native Images lösen die klassischen Schwachstellen von Java – Startzeit und Speicherverbrauch – effektiv. Eine einfache hello world-Binary startet in unter 10 ms, ein kompletter Spring Boot REST-Service in unter 100 ms. Der Preis ist eine längere Build-Zeit von mehreren Minuten und die Closed-World-Annahme, die dynamische Features einschränkt. Für Cloud-native Anwendungen, Serverless-Deployments (AWS Lambda, Google Cloud Functions) und CLI-Tools sind Native Images die natürliche Wahl. Mit der zunehmenden Integration in Frameworks wie Spring Boot, Micronaut und Quarkus wird der Einstieg immer einfacher – besonders die automatische AOT-Konfiguration von Spring Boot senkt die Einstiegshürde massiv.