Walk through the dynamic CDS flags: what do -XX:ArchiveClassesAtExit and -XX:SharedArchiveFile each do, and in what order?
answer
- ArchiveClassesAtExit = produce at exit
- SharedArchiveFile = consume/map
- Produce then consume, order matters
- onRefresh training run, extracted jar
- -Xshare:on to prove it's active; classpath+JDK must match
basics
~10 sFirst a training run with -XX:ArchiveClassesAtExit=app.jsa dumps an archive of the classes loaded, when the JVM exits. Then production runs pass -XX:SharedArchiveFile=app.jsa to memory-map and reuse that archive for faster startup.
solid answer
~40 sDynamic CDS is a two-phase flow. Phase 1, the training run: you start the app with `-XX:ArchiveClassesAtExit=application.jsa`; the JVM records every class loaded and, at clean exit, dumps their metadata into `application.jsa`. Phase 2, production: you start with `-XX:SharedArchiveFile=application.jsa`; the JVM memory-maps the archive instead of re-parsing/verifying those classes, cutting startup roughly in half. The order matters — you must create before you consume. For the training run to capture the right classes it should exercise the startup path; with Spring Boot you use `-Dspring.context.exit=onRefresh` so the context fully initializes then exits without serving traffic. Constraints: the classpath and JDK version must be identical between the two phases, or the archive is rejected. `-Xshare:on` forces failure if the archive can't be used (good for verifying it's active); the default `-Xshare:auto` silently falls back.
code
java · 15 lines// Two-phase dynamic CDS with a Spring Boot fat jar.
//
// 0) Extract for a stable classpath (recommended):
// java -Djarmode=tools -jar app.jar extract --destination app
// cd app
//
// 1) PRODUCE — training run, exits after context refresh:
// java -Dspring.context.exit=onRefresh \
// -XX:ArchiveClassesAtExit=application.jsa \
// -jar app.jar
//
// 2) CONSUME — production, reuse the archive every start:
// java -XX:SharedArchiveFile=application.jsa \
// -Xshare:on \ // fail fast if archive unusable (verify it's active)
// -jar app.jargo deeper
Remember it's two steps: make the .jsa on a training run, then point production at it.
Name both flags correctly, the exit-time dump semantics, and the classpath/JDK matching requirement.
Wires the flags into a CI build step and verifies with -Xshare:on / -Xlog:cds; knows onRefresh's role.
Owns the produce-once/consume-many pipeline in image builds and reasons about archive invalidation on dependency changes.
## The two dynamic-CDS flags ### `-XX:ArchiveClassesAtExit=<path>` — the producer (training run) Added by **JEP 350 (Dynamic CDS Archives, JDK 13)**. You run your app normally with this flag. As the JVM runs, it tracks every class it loads (app classes, library classes, and JDK classes not already in the default archive). When the JVM **exits cleanly**, it writes their internal metadata to `<path>` (a `.jsa` file). This replaced the old, painful static-CDS workflow of manually producing a class list with `-XX:DumpLoadedClassList` and then `-Xshare:dump`. Key point: the archive only contains classes that were **actually loaded** during that run. So the training run must exercise the code paths you care about — at minimum, application startup. ### `-XX:SharedArchiveFile=<path>` — the consumer (production run) On every subsequent start you pass this flag pointing at the same `.jsa`. The JVM **memory-maps** the archive and reuses the pre-parsed metadata for any class present in it, skipping locate/read/parse/verify for those classes. Result: faster startup, and the mapped region is shareable read-only across co-located JVMs. ### Order and lifecycle 1. **Produce**: `java -XX:ArchiveClassesAtExit=application.jsa ...` (must exit cleanly). 2. **Consume**: `java -XX:SharedArchiveFile=application.jsa ...` (as many times as you like). You cannot consume an archive you haven't produced. In CI/CD the produce step runs once during image build; the consume step runs in every container. ### Spring Boot specifics A raw training run would start the web server and block. Spring Boot 3.3+ adds **`-Dspring.context.exit=onRefresh`**: the `ApplicationContext` refreshes (all singletons instantiated, autoconfiguration resolved, classes loaded) and then the JVM exits *before* binding ports or serving requests. That gives a clean, fast, deterministic training run that still loads the classes needed to bootstrap. Recommended layout is the extracted jar (`java -Djarmode=tools -jar app.jar extract`) so the classpath is a stable set of files. ### Verifying and controlling CDS usage: `-Xshare` - `-Xshare:auto` (default): use the archive if usable, otherwise run normally and silently. Safe, but can hide a broken archive. - `-Xshare:on`: **fail** to start if the archive can't be mapped — use this in testing to prove CDS is actually engaged. - `-Xshare:off`: disable CDS entirely. Add `-Xlog:cds` (or `-Xlog:class+load=info`) to see what's mapped from the archive vs loaded normally. ### Gotchas - **Classpath must match** between produce and consume runs, in content and order. A different jar, added dependency, or reordering invalidates or under-uses the archive. - **JDK version must match** — an archive from JDK 21 won't be accepted by JDK 25. - A **non-clean exit** during the training run means no archive is written (`-XX:ArchiveClassesAtExit` dumps *at exit*). - CDS archives **startup classes**, not classes first loaded much later (e.g., a rarely hit endpoint) unless the training run exercised them.
- Why does the training run need -Dspring.context.exit=onRefresh instead of just starting normally?Without it the app boots the web server and blocks, so the JVM never exits and -XX:ArchiveClassesAtExit never dumps. onRefresh initializes the full context (loading the bootstrap classes) then exits cleanly, giving a deterministic, non-blocking training run.
- What happens at production start if the classpath changed since the training run?The archive is invalidated or only partially usable. With -Xshare:auto the JVM silently falls back to normal loading; with -Xshare:on it refuses to start. You must re-run the training step after any dependency/classpath change.
saying these in an interview costs you the question
- Swapping the two flags' roles (thinking SharedArchiveFile creates the archive)
- Not knowing the archive is written at JVM exit, so a hung/killed training run yields nothing
- Ignoring that classpath and JDK must match between produce and consume
- Assuming a normal (non-onRefresh) Spring Boot run makes a good training run despite it blocking on the server