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.
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 RAMThe 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 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.
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.