Checkpoint/restore can do more than reduce startup and warm-up time. OpenJ9 CRIU support lets Java application teams use a warmed JVM as a reusable runtime baseline while retaining standard JVM capabilities for diagnostics and incident response after restore.

Checkpoint/restore is often presented as a cold-start optimization: initialize a Java service once, capture its state, then restore ready-to-run instances when demand arrives. That is valuable for elastic workloads, but it is only part of the operational story.

A restored JVM is an application resumed from a known point in its lifecycle. It may already have loaded classes, initialized its framework, populated caches, and compiled hot methods. The relevant operational questions therefore change: can an operator enable additional diagnostics for one restore? Can a warmed instance be investigated without repeating a cold start?

OpenJ9 CRIU support is the JVM-level checkpoint/restore capability discussed in this article. It is exposed through the CRIUSupport API and underpins InstantOn-style deployment workflows, including Open Liberty InstantOn. This article focuses on the OpenJ9 CRIU API and restore-time JVM behaviour. CRIUSupport does not turn a checkpoint image into a universally portable binary, and it does not remove the need to manage application-owned resources. Instead, it offers a more useful model: a warmed Java runtime baseline whose restore-time behaviour can still be controlled responsibly.

A checkpoint image is a baseline, not a finished deployment

An image created in one environment is not automatically ready for every other environment. The restore environment might use a different diagnostics profile, container limit, shutdown model, or incident-response policy.

The useful mental model is therefore:

Warm once; restore with deliberate operational controls.

Checkpoint/restore lifecycle. During the build phase, the application is initialized and ideally exercised with production-like load. Checkpoint hooks run before the warmed JVM state is captured. In production, OpenJ9 restores the image, reads any supported restore-time options, runs the restore hooks, and resumes the application so that it can become ready for traffic.

The checkpoint image contains reusable runtime state. Restore-time inputs and restore hooks determine how that state should operate in the target environment.

Restore-time controls in OpenJ9

The application registers only the paths where OpenJ9 should look for restore-time inputs. The referenced files do not have to exist when the checkpoint is taken; they can be created, replaced, or updated in the target environment before the checkpoint image is restored.

The application can register:

  • An options file containing supported restore-time JVM options.
  • An environment file containing environment variables to add after restore. The special OPENJ9_RESTORE_JAVA_OPTIONS variable can also supply supported JVM options.
CODE
import java.nio.file.Path;
import org.eclipse.openj9.criu.CRIUSupport;

CRIUSupport criu = CRIUSupport.getCRIUSupport()
    .setImageDir(Path.of("/opt/checkpoint"))
    .registerRestoreOptionsFile(Path.of("/opt/runtime/restore.options"))
    .registerRestoreEnvFile(Path.of("/opt/runtime/restore.env"));

criu.checkpointJVM();

Applications do not necessarily need code changes to register these paths. OpenJ9 also recognizes system properties for the restore-options and environment files:

CODE
-Dopenj9.internal.criu.optionsFile=/opt/runtime/restore.options \
-Dopenj9.internal.criu.envFile=/opt/runtime/restore.env

Whether the paths are supplied through the CRIUSupport API or through system properties, they tell OpenJ9 where to look at restore time. The referenced files can be absent when the checkpoint is created and supplied later in the restore environment.

CODE
# /opt/runtime/restore.options
-Xshareclasses:disableOnRestore
-Xtrace:print={j9vm}

# /opt/runtime/restore.env
APP_CONFIG_FILE=/etc/myapp/region-a.properties
OPENJ9_RESTORE_JAVA_OPTIONS=-Xshareclasses:disableOnRestore -Xtrace:print={j9vm}

Environment-file boundary: registerRestoreEnvFile is additive. Environment variables captured in the checkpointed JVM are retained. Entries in the restore environment file are added only when they do not modify an existing variable, so the file is not a general environment-override mechanism.

Only a defined subset of JVM options can be supplied after restore. Options that establish fundamental VM state, such as the GC policy, compressed-reference mode, heap-region layout, or arbitrary new -Xms and -Xmx values, cannot generally be changed after that state has been captured.

Which controls are useful after restore?

[TABLE]

The JIT compiler cannot always be re-enabled after restore. If the JVM was checkpointed with -Xint, or with both JIT and AOT disabled, restore-time options do not recreate the compiler state that was absent from the checkpoint. Similarly, supplying -Xaot after restore does not re-enable AOT if the checkpoint was created with -Xnoaot.

GC and heap controls selected before checkpoint

Some controls influence the restored JVM but cannot be newly supplied through the restore-options file. They must be selected when the checkpoint image is created.

Operational needCheckpoint-time controlEffect after restore
Reduce GC-thread restoration overhead-XX:CheckpointGCThreads=<number>Reduces the number of GC threads retained when the checkpoint image is created. At restore, OpenJ9 can increase the count using a restore-time -Xgcthreads value or the default appropriate for the target environment, but it cannot reduce the count below the checkpoint value.
Adapt heap limits to available memory-XdynamicHeapAdjustmentAllows OpenJ9 to adjust the effective minimum and maximum heap sizes so that they remain within the physical-memory limits of the checkpoint and restore environments. The option must be enabled at checkpoint and applies to both checkpoint and restore.

Without dynamic heap adjustment, the heap size and heap regions established at checkpoint remain part of the restored JVM state. For example, an image created in a container without a memory limit does not automatically adopt a smaller limit when it is restored in another container.

-XdynamicHeapAdjustment provides an opt-in mechanism for checkpoint images that might be restored into environments with different memory capacities. It does not turn restore into a second JVM startup or permit arbitrary replacement of every heap and GC setting.

This distinction is important: restore-time configuration is a controlled adjustment of an already-running JVM, not a second JVM command line.

Debugging a restored JVM

A fault might appear only after the application is warm, a cache has been populated, or a particular restore environment is active. OpenJ9 allows a debugger to be attached after the checkpoint image is restored.

One approach is to configure JDWP before checkpoint and use the OpenJ9-specific suspendOnRestore parameter to control whether the restored application waits for the debugger:

CODE
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005,suspendOnRestore=y

Here, suspend=n allows the application to run normally before checkpoint, while suspendOnRestore=y suspends it after restore until the debugger is connected and ready.

Alternatively, when the JVM is started with -XX:+DebugOnRestore, the JDWP configuration can be supplied through the restore-time options so that debugging is enabled for the restored JVM.

After restore, the debugger is attached through the standard Java JDWP workflow. As with any JDWP configuration, expose the debug connection only in an appropriately secured environment.

Conclusion: fast startup with operational control

Checkpoint/restore changes the unit of deployment. Instead of deploying only application code and a cold JVM, teams can deploy a warmed runtime baseline together with controlled restore-time configuration.

OpenJ9 CRIU support is therefore not only about reaching readiness sooner. It retains selected JVM tuning, diagnostic, and debugging capabilities when a warmed Java process becomes a reusable deployment artifact.

References

  1. OpenJ9 CRIU support
  2. OpenJ9 JDK 26 CRIUSupport API
  3. OpenJ9 -Xrunjdwp and suspendOnRestore
  4. OpenJ9 compiler support for Debug On Restore
  5. OpenJ9 -XX:CheckpointGCThreads
  6. OpenJ9 -XdynamicHeapAdjustment