What is CRaC, and what does Spring do to your beans when a checkpoint is taken and when the app is restored?
answer
- Coordinated Restore at Checkpoint = JVM snapshot
- beforeCheckpoint -> stop(), afterRestore -> start()
- release pools/sockets before snapshot
- needs CRaC-enabled JDK
- context registered as org.crac.Resource
basics
~20 sCRaC (Coordinated Restore at Checkpoint) snapshots a running JVM to disk and restores it later for near-instant startup. On checkpoint Spring calls stop() on its Lifecycle beans (closing pools/connections); on restore it calls start() to reopen them.
solid answer
~40 sCRaC = Coordinated Restore at Checkpoint, an OpenJDK feature (needs a CRaC-enabled JDK) that dumps a running JVM's memory to a snapshot and later restores it, so a warmed-up app resumes in tens of milliseconds. Spring Framework 6.1+ / Spring Boot 3.2+ integrate by registering the application context as a CRaC resource. Just before the snapshot Spring drives its Lifecycle/SmartLifecycle beans through stop(), which releases things that can't survive a snapshot — JDBC connection pools, open sockets. After restore Spring calls start() again to reopen them. The idea: build once, warm up, snapshot, then restore many fast-starting instances that reconnect to fresh infrastructure.
code
java · 27 linesimport org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
// Spring calls stop() before the checkpoint and start() after restore.
@Component
public class ExternalConnection implements SmartLifecycle {
private volatile boolean running;
private Client client; // holds a live socket/pool
@Override
public void start() { // invoked on restore (afterRestore)
this.client = Client.open();
this.running = true;
}
@Override
public void stop() { // invoked on checkpoint (beforeCheckpoint)
if (client != null) client.close();
this.running = false;
}
@Override
public boolean isRunning() {
return running;
}
}go deeper
Know the one-liner: CRaC snapshots a running JVM; Spring stops beans before and starts them after so connections are released/reopened.
Be able to name the two callbacks (beforeCheckpoint/afterRestore) and map them to stop()/start(), and that a CRaC JDK is required.
Explain the coordination requirement (no open FDs allowed) and how Spring's DefaultLifecycleProcessor registers as an org.crac.Resource.
Frame CRaC as a startup-latency alternative to native image and reason about its operational trade-offs.
**CRaC** stands for *Coordinated Restore at Checkpoint*. It is an OpenJDK project that lets you take a snapshot ("checkpoint") of a fully running JVM — its heap, threads, JIT-compiled code, everything — write it to disk, and later "restore" a brand-new process from that image. Because the JVM comes back already warmed up (classes loaded, JIT-optimized, caches populated), startup drops from seconds to tens of milliseconds. It requires a **CRaC-enabled JDK** (e.g. Azul Zulu with CRaC); a stock JDK cannot do it. **The coordination problem.** A memory snapshot cannot capture live OS resources such as open TCP sockets, file descriptors, or database connections — on restore those handles would be stale/invalid, and the OS on the restore machine is different anyway. CRaC therefore does not silently snapshot everything: it demands that open resources be *closed before* the checkpoint and *reopened after* restore. If open file descriptors remain that nobody registered, CRaC raises a `CheckpointException`. This is where the word *Coordinated* comes from — the runtime coordinates with the application so state is released and reacquired around the snapshot. **How Spring plugs in.** Spring Framework 6.1 (Spring Boot 3.2) added first-class CRaC support. Spring depends on the small `org.crac:crac` facade library, which delegates to the JDK's CRaC API when present and is a no-op otherwise. Spring's `DefaultLifecycleProcessor` registers the application context as an `org.crac.Resource` on the global CRaC context. The `org.crac.Resource` interface has two callbacks: `beforeCheckpoint(Context)` and `afterRestore(Context)`. - On **`beforeCheckpoint`**, Spring stops the context's lifecycle: it invokes `stop()` on all running `Lifecycle`/`SmartLifecycle` beans, in **descending phase order** (the reverse of startup). Well-behaved beans use `stop()` to close connection pools, shut network listeners, and flush buffers so no live OS resources remain. - On **`afterRestore`**, Spring calls `start()` on those beans, in **ascending phase order**, so pools reopen and listeners rebind — now against whatever infrastructure the restored instance sees. **Two ways to trigger a checkpoint.** (1) *On demand* while the app runs: `jcmd <pid> JDK.checkpoint`, with the JVM started using `-XX:CRaCCheckpointTo=<dir>`. (2) *Automatically at startup*: set the Spring property `spring.context.checkpoint=onRefresh`, which makes Spring take a checkpoint at the end of context refresh. Restore in both cases with `java -XX:CRaCRestoreFrom=<dir>`. **When to use it.** CRaC is an alternative to GraalVM native image for fast startup and fast scale-to-zero / serverless. Unlike native image it keeps the full JVM (dynamic proxies, reflection, JIT peak performance), but it needs a special JDK and careful handling of external state. **Key terms.** *Lifecycle* — a Spring interface with `start()`/`stop()`/`isRunning()`. *SmartLifecycle* — extends it with `getPhase()` and auto-start. *Checkpoint* — the snapshot. *Restore* — booting a process from it.
- Can you take a CRaC checkpoint on a regular OpenJDK build?No. CRaC requires a CRaC-enabled JDK (for example Azul Zulu with CRaC). The `org.crac` facade compiles everywhere but only performs a real checkpoint on a CRaC runtime; otherwise it's a no-op.
- Why doesn't Spring just let CRaC snapshot the open connections as-is?A memory snapshot can't preserve live OS handles — sockets/file descriptors would be stale on restore and would point at infrastructure that may no longer exist. CRaC also fails the checkpoint if unmanaged FDs are open, so Spring closes them via stop() and reopens via start().
saying these in an interview costs you the question
- Thinking CRaC works on any JDK without a special build
- Believing open DB connections are snapshotted and 'just work' after restore
- Confusing CRaC (full JVM snapshot) with GraalVM native image (ahead-of-time compiled binary)