What do you gain by compiling a Spring Boot app to a GraalVM native image instead of running it on the JVM, in terms of startup and memory?
answer
- AOT machine code, no JVM boot
- tens of ms startup vs seconds
- low RSS: no JIT, small heap
- nativeCompile / bootBuildImage
- great for serverless / scale-to-zero
basics
~10 sA native image starts in tens of milliseconds instead of seconds, and uses much less memory (RSS), because the app is compiled ahead-of-time to a native executable with no JVM warm-up.
solid answer
~40 sGraalVM native image compiles the app ahead-of-time (AOT) into a standalone machine-code executable. Because there is no JVM to boot, no bytecode to load and verify, and no JIT warm-up, startup drops from seconds to tens of milliseconds. Resident memory (RSS) is also much lower — often well under half — since there is no JIT compiler, less class metadata, and a small default heap. Spring Boot enables this via its AOT engine plus the GraalVM Native Build Tools (`./gradlew nativeCompile` or `bootBuildImage`). This makes native images attractive for serverless/functions, CLIs, and horizontally scaled services where fast scale-to-zero and cold-start matter, and where per-instance memory cost dominates.
code
kotlin · 11 lines// build.gradle.kts — enable GraalVM Native Build Tools
plugins {
id("org.springframework.boot") version "3.3.0"
id("org.graalvm.buildtools.native") version "0.10.2" // native image support
}
// Build a native executable (requires a GraalVM JDK):
// ./gradlew nativeCompile -> build/native/nativeCompile/<app>
// Or build a native container image (no local GraalVM needed):
// ./gradlew bootBuildImage
// Spring Boot's AOT engine runs automatically during the native build.go deeper
Must know the headline: fast startup (tens of ms) and low memory, because it's AOT-compiled with no JVM warm-up.
Should connect low RSS to 'no JIT + small heap + less metadata' and name the build commands (nativeCompile / bootBuildImage).
Should frame it as a trade: gains startup/footprint, pays in peak throughput and build complexity; pick per workload.
Should tie startup/footprint to cost models (serverless cold-start SLAs, per-pod RAM * replica count) and fleet economics.
## AOT vs JIT - A normal Spring Boot app runs on the JVM: the JVM boots, loads and verifies bytecode, and initially *interprets* it. A Just-In-Time (JIT) compiler then watches which methods run often ('hot') and compiles them to optimized machine code at runtime — this is 'warm-up'. - **GraalVM native image** does the opposite: at build time it runs Ahead-Of-Time (AOT) compilation, producing a single self-contained native executable that already contains machine code plus a minimal runtime. ## Why startup is fast There is: - no JVM process to start, - no classpath scanning of bytecode, - no class verification, - and no interpretation phase. Spring Boot additionally moves work to build time: the **Spring AOT engine** pre-computes the bean definitions and generates code, so the context 'refresh' does far less at runtime. Result: startup measured in *tens of milliseconds* rather than the ~1–5 seconds a typical JVM Boot app needs. ## Why memory (RSS) is low **RSS** = Resident Set Size, the physical RAM a process actually uses. On the JVM, RAM is consumed by: - the JIT compiler, - compiled-code caches, - extensive class metadata, - and a heap sized generously for throughput. A native image ships no JIT compiler, stores far less metadata, and defaults to a low-footprint garbage collector (Serial GC) with a small heap — so RSS is typically less than half of the equivalent JVM process. ## How you build it Add the GraalVM `org.graalvm.buildtools.native` plugin (Spring Initializr adds it). Then: - `./gradlew nativeCompile` (needs a GraalVM JDK installed) or - `./gradlew bootBuildImage` (builds inside a container, no local GraalVM needed). Spring Boot's AOT processing runs automatically as part of the native build. ## When to use it Best for: - serverless/FaaS where cold-start latency is user-visible; - CLI tools; - and fleets of small horizontally-scaled instances where per-pod RAM cost dominates. **Trade-offs (covered in sibling questions):** lower peak throughput because there is no runtime JIT, a closed-world model that breaks unregistered reflection, and slow, memory-hungry builds.
- Does a native image use less memory only at startup, or throughout its life?Throughout. There is no JIT compiler or compiled-code cache, less class metadata, and a small default heap, so steady-state RSS stays lower than an equivalent JVM process — though a JVM can eventually GC down too.
- Name one workload where native's startup advantage is essentially wasted.A long-running, high-throughput backend that starts once and runs for days. It starts once, so tens-of-ms startup barely matters, while it loses the JIT's peak-throughput advantage — the JVM is usually the better fit there.