What happens to a CDS archive when the classpath or JDK changes, and how do you verify CDS is actually active at runtime?
answer
- archive bound to classpath + JDK build
- mismatch -> silent fallback (auto)
- -Xshare:on = fail loudly / prove it's on
- -Xshare:off = disable, for A/B
- -Xlog:cds to diagnose
basics
~20 sThe archive is tied to the exact classpath and JDK it was built with. If either changes, the JVM detects a mismatch, ignores the archive, and falls back to normal class loading (no crash). Verify with -Xshare:on (fails if unusable) or -Xlog:cds logging.
solid answer
~50 sA CDS archive captures a specific set of classes from a specific classpath, built by a specific JDK. At runtime the JVM validates the archive against the current classpath and JVM; if dependencies changed, the JDK version differs, or the classpath layout differs, the archive is considered stale and the JVM **silently falls back** to ordinary class loading — you lose the speedup but nothing breaks. That silent fallback is a gotcha: you can think CDS is on when it isn't. To *verify*, use `-Xshare:on`, which forces CDS and makes the JVM **fail to start** if the archive can't be mapped — good for catching regressions in tests/CI. `-Xshare:auto` (the default) uses the archive if valid and falls back otherwise; `-Xshare:off` disables CDS entirely. For diagnostics, `-Xlog:cds` (or `-Xlog:cds=debug`) prints whether the archive was used and why classes were or weren't loaded from it. Because of this coupling, you must **regenerate the archive** whenever you change dependencies or upgrade the JDK.
code
java · 12 lines// Prove CDS is active (fail startup if archive unusable) — great for CI:
// java -Xshare:on -XX:SharedArchiveFile=application.jsa -jar app/my-app.jar
//
// See exactly what CDS did:
// java -Xlog:cds -XX:SharedArchiveFile=application.jsa -jar app/my-app.jar
//
// Measure the benefit by disabling CDS:
// java -Xshare:off -jar app/my-app.jar
//
// Auto-create/refresh a stale archive (newer JDKs):
// java -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=application.jsa \
// -jar app/my-app.jargo deeper
Just know the archive can become stale and the app still runs without it.
Know that changing dependencies or the JDK invalidates the archive and you must regenerate it.
Explain silent fallback under -Xshare:auto, use -Xshare:on to enforce/verify, and -Xlog:cds to diagnose.
Bake archive validation into CI (-Xshare:on), align build/runtime JDKs, treat the .jsa as a versioned build artifact, and consider AutoCreateSharedArchive for self-healing.
## Why the archive is fragile by design A `.jsa` archive stores the JVM's parsed metadata for a **specific class list resolved from a specific classpath**, dumped by a **specific JDK build**. For the memory-mapped metadata to be safe to reuse, the runtime environment must match what produced it. So the JVM records identifying information (JDK version/build, classpath entries and their state) and **validates** it at startup. Mismatches that invalidate (or partially invalidate) an archive include: - **JDK version/build change** — even a minor upgrade can change internal class-metadata layout. - **Classpath changes** — added/removed/updated dependency jars, or a different classpath order/layout. - **Different module/agent configuration** in some cases. ## The fallback is silent — the key gotcha Under the default `-Xshare:auto`, if the archive is invalid the JVM does **not** error. It just **ignores the archive and loads classes normally**. Your app still works — you simply lose the startup benefit, quietly. This is the classic trap: a dependency bump silently disables CDS and nobody notices the regression. ## The `-Xshare` modes - **`-Xshare:auto`** (default): use the archive if valid, otherwise fall back silently. - **`-Xshare:on`**: *require* CDS — if the archive can't be used, the JVM **refuses to start**. Use this in tests/CI to *prove* the archive is valid and catch regressions early. - **`-Xshare:off`**: disable CDS entirely (useful for A/B measuring the benefit). ## Diagnosing with unified logging Use JVM unified logging to see what CDS actually did: ``` java -Xlog:cds -XX:SharedArchiveFile=application.jsa -jar app/my-app.jar # or more detail: java -Xlog:cds=debug ... ``` This reports whether the shared archive was mapped, and can show classes that could not be loaded from the archive (e.g., because their source changed). ## Operational consequences 1. **Regenerate on every dependency or JDK change.** Treat the `.jsa` like a derived build artifact tied to a specific build input; rebuild it in the same pipeline that builds the jar. This is exactly why baking it in at build time (or via `BP_JVM_CDS_ENABLED=true` buildpacks) is preferred over hand-generating it once. 2. **Match the runtime JDK to the build JDK.** If your CI builds the archive with one JDK and the container runs a different one, CDS quietly disables. 3. **Verify in CI with `-Xshare:on`** so a stale archive fails loudly instead of degrading silently. 4. **Measure both ways** with `-Xshare:off` vs on to confirm the archive is delivering the expected startup reduction. ## Newer convenience: auto-create/refresh Modern JDKs also offer `-XX:+AutoCreateSharedArchive` combined with `-XX:SharedArchiveFile=app.jsa`, which creates the archive on first run and recreates it automatically if it becomes stale — reducing the manual regeneration burden, though a build-time archive is still the norm for production determinism.
- Your startup times crept back up after a Spring dependency upgrade — what's the likely cause?The dependency change invalidated the CDS archive; under -Xshare:auto the JVM silently fell back to normal loading. Regenerate the archive from the new classpath and, ideally, run -Xshare:on in CI so it fails loudly next time.
- How would you gate a build so a broken CDS archive can't ship?Add a smoke step that launches the app with -Xshare:on -XX:SharedArchiveFile=...; if the archive is stale or classpath-mismatched, the JVM refuses to start and the build fails.
saying these in an interview costs you the question
- Claiming a classpath/JDK mismatch crashes the app — by default it just silently falls back.
- Assuming one archive is valid forever across dependency and JDK upgrades.
- Confusing -Xshare:on (require CDS) with -Xshare:off (disable CDS).
- Not knowing there's any way to verify CDS is actually being used.