Loslegen
Javaneer
Zurück zur Stufe
Modul 8·Produktion & modernes Spring

GraalVM-Native-Images

Eine Spring-Boot-App vorab (AOT) in eine native Binärdatei kompilieren: Start in Millisekunden und wenig Speicher, mit den zugehörigen Kompromissen.

14 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

A Spring Boot app on the JVM starts in a second or two and warms up as the JIT compiler optimizes hot paths. That's fine for a long-running server - but painful when you want to scale to zero and back, run serverless functions, or pack many small services densely. GraalVM native images compile your app ahead of time into a standalone binary that starts in milliseconds and uses a fraction of the memory.

Ahead-of-time, not just-in-time

Normally Java compiles source to bytecode, and the JVM's JIT compiler turns hot bytecode into machine code while the app runs. GraalVM's native-image tool instead does AOT (ahead-of-time) compilation: at build time it analyzes your whole app and produces a native executable containing only the code that's reachable - no JVM needed at runtime:

# Spring Boot's build plugin drives GraalVM native-image
./mvnw -Pnative native:compile
./target/bookvault            # a native binary - starts in ~50ms, tens of MB of RAM

The results are dramatic: startup drops from ~1-2s to tens of milliseconds, and memory falls several-fold. For a service that scales up and down constantly, or a function billed per-invocation, that changes the economics.

The catch: a closed world

AOT's power comes from a closed-world assumption - at build time the tool must see all the code that could run. Java features that decide things at runtime fight this:

  • Reflection, dynamic proxies, and runtime class loading must be known ahead of time (declared in hints), or the code isn't included and fails at runtime.
  • Builds are slow (minutes) and memory-hungry compared with a normal compile.
  • You lose the JIT's peak-throughput optimizations and the rich runtime tooling that attaches to a JVM.

The good news: Spring Boot 3+ was re-architected for exactly this. Its AOT engine and the Spring libraries generate the necessary reachability hints for you, so a standard Spring app usually goes native without manual configuration. When you use reflection in your own code, you supply hints via RuntimeHintsRegistrar.

<!-- enable the GraalVM native build support -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
A pop-up stall vs. a full restaurant kitchen

A full restaurant kitchen (the JVM) can cook anything on demand - flexible, but it takes time to fire up and heat every station. A pop-up stall built for one menu (a native image) is prepped ahead for exactly the dishes it will serve: it opens in seconds and needs a tiny footprint - but it can't suddenly cook something that wasn't planned. AOT trades the JVM's runtime flexibility for instant, lean startup by deciding everything in advance.

Native isn't automatically better

For a long-running service, the JVM's fast peak throughput and mature tooling often win, and its 1-2s startup is irrelevant. Native images shine when startup time and memory dominate: serverless, scale-to-zero, CLIs, and very dense deployments. Choose per workload - don't go native by default.

When is native worth the trade-offs?

Two BookVault deployments: (a) a steady API server that runs 24/7 behind a load balancer, and (b) a scheduled function that wakes a few times an hour to send overdue notices, then exits. For each, decide whether a GraalVM native image is worth it, and justify it in terms of startup, memory, and throughput.

What is the main benefit of a GraalVM native image, and its central trade-off?

Key takeaways

  • GraalVM native-image compiles a Spring Boot app ahead of time into a standalone binary - no JVM at runtime.
  • Startup drops from ~1-2s to tens of milliseconds and memory falls several-fold - great for serverless, scale-to-zero, and dense deployments.
  • The trade-off is a closed-world model: reflection/proxies/dynamic loading need build-time hints, builds are slow, and you lose JIT peak throughput.
  • Spring Boot 3+ generates most reachability hints automatically, so typical apps go native without manual config.
  • It isn't universally better: long-running servers often prefer the JVM's throughput and tooling. Choose per workload.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten