When would you choose CDS over a GraalVM native image (or plain JVM) for a Spring Boot service, and how do CDS and AOT relate?
answer
- Three tiers: plain JVM < CDS(+AOT) < native
- CDS keeps full dynamism + JIT, cheap builds
- Native = closed-world, long build, no JIT, sub-100ms
- AOT (build transform) + CDS (class-load cache) stack
- CDS for many-replica autoscale; native for serverless/scale-to-zero
basics
~20 sChoose CDS when you want faster startup and lower memory but must keep full JVM dynamism (reflection, proxies, agents) and cheap, fast builds. Native image starts far faster but has a closed-world model and long builds. CDS and AOT are complementary and stack on the JVM.
solid answer
~50 sCDS is the pragmatic middle tier. Plain JVM: slowest start, maximum flexibility, trivial builds. GraalVM native image: sub-100ms start and tiny footprint, but a closed-world model (reflection/proxies/resources need reachability metadata), minutes-long builds, no JIT peak throughput, and weaker tooling/agents. CDS sits between: ~2x faster start and lower per-instance memory while remaining a normal HotSpot JVM — reflection, dynamic proxies, `@Conditional` beans, Java agents and full JIT all still work, builds stay fast, and there's no reachability metadata to maintain. So choose CDS when moderate startup wins matter (autoscaling, many replicas, CI) but you can't accept native's constraints or build cost. Choose native when startup/footprint are dominant (serverless, scale-to-zero) and you'll invest in hints/testing. Importantly, CDS is not either/or with **Spring AOT**: AOT pre-computes bean definitions and generates code at build time, CDS caches class-loading; used together on the JVM they stack, and AOT is also the foundation native image builds on.
code
java · 14 lines// AOT + CDS stack on the JVM (best effort/reward for many services):
//
// Build an AOT-processed image with buildpack CDS training baked in:
// ./mvnw -Pnative? NO -- stay on JVM:
// BP_JVM_CDS_ENABLED=true ./mvnw spring-boot:build-image \
// -Dspring-boot.aot.enabled=true // AOT process at build time
//
// Runtime (AOT jar + CDS archive both engaged):
// java -Dspring.aot.enabled=true \
// -XX:SharedArchiveFile=application.jsa \
// -jar app.jar
//
// Native image is the *other* branch off the same AOT groundwork:
// ./mvnw -Pnative native:compile // minutes-long, closed-world resultgo deeper
Know CDS = faster JVM startup while staying a normal JVM; native = much faster but restricted.
Contrast startup/build/flexibility across the three tiers and state CDS keeps reflection/JIT.
Gives a concrete decision guide and knows AOT and CDS are complementary and stack.
Owns the strategy: maps workload shape (autoscale vs scale-to-zero, throughput vs cold-start) to tier, and treats AOT as shared groundwork for both JVM-CDS and native.
## Three tiers of the Spring Boot startup/footprint spectrum ### 1. Plain JVM - Slowest startup (parse/verify/load everything each boot; JIT warms up over time). - Maximum flexibility, trivial build, best tooling. ### 2. JVM + CDS (+ optionally AOT) - **~2x faster startup**, lower per-instance memory (shared, mapped class metadata). - **Still a full HotSpot JVM**: reflection, `java.lang.reflect.Proxy`/CGLIB proxies, runtime classpath scanning, `@Conditional`/profile beans resolved at runtime, Java agents (APM, debuggers), and full JIT for peak throughput — all unchanged. - **Cheap, fast builds**; no reachability metadata, no `reflect-config.json`. - Cost: a training run in the pipeline; archive must be re-trained on dependency/JDK changes. ### 3. GraalVM native image - **Sub-100ms startup**, very low memory, small image. - **Closed-world assumption**: everything reachable must be known at build time. Reflection, proxies, resources and serialization need **reachability metadata** (Spring AOT + GraalVM reachability metadata generate much of it, but edge cases need hints like `@RegisterReflectionForBinding` / `RuntimeHintsRegistrar`). - **Minutes-long builds**, higher memory to build, **no JIT** (AOT-compiled, so lower peak throughput than a warmed JVM), and reduced agent/tooling support. ## How CDS and Spring AOT relate (not either/or) - **Spring AOT** (`spring-boot-maven-plugin:process-aot` / Gradle `processAot`, run at build time) transforms the application: it pre-computes **bean definitions**, generates Java source for the context, and produces GraalVM hints. It removes reflection-heavy work from startup. - **CDS** caches the **class-loading/verification** cost. These address different costs, so on the JVM they **stack**: AOT-processed jar + a CDS archive starts faster than either alone. AOT is *also* the mandatory groundwork native image compiles from. So the mental model is: AOT is a build-time transform usable on both JVM and native; CDS is a JVM-only startup accelerator; native image is the closed-world endpoint. ## A decision guide Choose **CDS (JVM)** when: - You run **many replicas** or **autoscale** and want quicker rollouts/scale-out without native's constraints. - You depend on **reflection/proxies/agents** or heavy dynamic configuration that's painful under closed-world. - **Build time and simplicity** matter (CI, frequent deploys), and moderate (~2x) startup gains are enough. - You want **peak throughput** preserved (JIT). Choose **native image** when: - **Startup and memory are dominant** — serverless/FaaS, scale-to-zero, very short-lived jobs, high-density packing. - You accept **long builds** and the effort of maintaining hints and native-specific testing. Stay **plain JVM** when startup/footprint simply don't matter and you value zero extra pipeline steps. ## Practical notes / gotchas - CDS improves **startup**, not steady-state throughput; native trades peak throughput for instant start. - CDS needs **classpath + JDK stability** between training and production; native bakes everything at build. - Combining **AOT + CDS on the JVM** is often the best effort/reward ratio: meaningful startup cut, full dynamism retained, modest pipeline cost. - Don't market CDS as 'native-like startup' — it's a fraction of native's speedup with none of its constraints.
- A team wants sub-100ms cold starts for a scale-to-zero serverless function. CDS or native?Native image. CDS only halves startup (still hundreds of ms to seconds), whereas native reaches tens of ms and tiny footprint — the right fit for scale-to-zero, provided the team accepts long builds, reachability hints, and reduced runtime dynamism.
- Are Spring AOT and CDS competing choices?No. AOT is a build-time transform (pre-computed bean definitions + generated code + hints) usable on JVM and required for native; CDS caches class-loading at runtime on the JVM. They address different costs and stack, so AOT + CDS on the JVM is a common combination.
- Why does CDS keep peak throughput while native image can lose some?CDS is still HotSpot with the full JIT, so hot code is profiled and optimized at runtime. Native image is AOT-compiled ahead of time with no JIT, so it can't apply profile-guided runtime optimizations, giving lower peak throughput despite instant startup.
saying these in an interview costs you the question
- Calling CDS 'native-like' or claiming it removes the JVM
- Saying CDS needs reachability/reflect metadata like native image
- Treating AOT and CDS as competing alternatives instead of stacking layers
- Recommending native image universally, ignoring build cost and closed-world constraints