[crac] RFR: [CRaC] Support checkpointing with --patch-module by treating patch JARs as persistent

mazhen duke at openjdk.org
Mon Aug 4 18:42:10 UTC 2025


#### Summary

This change enhances CRaC's checkpointing capabilities to correctly handle applications launched with the `--patch-module` option. Previously, using a JAR file with `--patch-module` would cause checkpointing to fail due to unmanaged file descriptors. This patch resolves the issue by treating these JARs as persistent resources, similar to how classpath JARs are handled.

#### Problem

When a JAR file is specified via `--patch-module`, it is opened by two distinct mechanisms within the JVM:

1.  **Native `ClassLoader`**: The C++ `ClassLoader` implementation opens the JAR file to build its internal class path structures. This results in a file descriptor that is not managed by CRaC's Java-level resource tracking, leading to it being flagged as a "BAD: opened by application" file during `check_fds`.
2.  **Java `ModulePatcher`**: The Java-level `jdk.internal.module.ModulePatcher` also opens the same JAR to scan for packages and resources. While this could be made CRaC-aware, the native-level file descriptor remains an issue.

This dual-opening mechanism leads to `CheckpointOpenFileException` being thrown for both file descriptors during a checkpoint, preventing a successful image creation. The log output clearly illustrates this problem:


$ jcmd 3403046 JDK.checkpoint
3403046:
...
JVM: FD fd=4 type=regular path="/home/mazhen/works/patch-demo/legacy-patch.jar" BAD: opened by application
JVM: FD fd=5 type=regular path="/home/mazhen/works/patch-demo/legacy-patch.jar" OK: claimed by java code
...
An exception during a checkpoint operation:
jdk.internal.crac.mirror.CheckpointException
    Suppressed: jdk.internal.crac.mirror.impl.CheckpointOpenFileException: legacy-patch.jar
        at java.base/jdk.internal.crac.JDKFileResource.lambda$beforeCheckpoint$0(JDKFileResource.java:90)
        ...
    Caused by: java.lang.Exception: This file descriptor was created by main at epoch:1752027938791 here
        at java.base/jdk.internal.crac.JDKFdResource.<init>(JDKFdResource.java:40)
        ...
        at java.base/jdk.internal.module.ModulePatcher$JarResourceFinder.<init>(ModulePatcher.java:433)
        ...
    Suppressed: jdk.internal.crac.mirror.impl.CheckpointOpenFileException: FD fd=4 type=regular path=/home/mazhen/works/patch-demo/legacy-patch.jar
        at java.base/jdk.internal.crac.mirror.Core.translateJVMExceptions(Core.java:115)
        ...


#### Solution

The fundamental assumption behind this solution is that JARs provided via `--patch-module` should be treated as persistent resources, just like those on the classpath. It is expected that these files will be available at the same location after a restore.

This PR implements a two-pronged approach to address both file handles:

1.  **Java-level Fix**: The `jdk.internal.module.ModulePatcher$JarResourceFinder` is modified to use `jdk.internal.util.jar.PersistentJarFile` instead of the standard `java.util.jar.JarFile`. This ensures that the file handle opened by the Java module system is registered as a CRaC `JDKResource`, making it a managed and permissible resource during checkpointing.

2.  **Native-level Fix**: To handle the file descriptor opened by the native `ClassLoader`, we introduce a new mechanism to whitelist it:
    *   A new function, `ClassLoader::get_patch_module_entry_fds()`, is added in `classLoader.cpp`. This function iterates through the `_patch_mod_entries` and collects the file descriptors of all associated JAR files.
    *   In `crac_linux.cpp`, the `VM_Crac::check_fds` function now calls this new method to retrieve the list of patch-module file descriptors. These FDs are then added to the allowlist, and `check_fds` will correctly identify them as `OK: claimed by patch-module`, thus avoiding the exception.

By implementing these changes, we ensure that both file descriptors associated with a patched module's JAR are correctly recognized as persistent and valid, allowing the checkpoint to proceed successfully.

-------------

Commit messages:
 - style: Add final newline to PatchModuleTest.java
 - Apply review suggestions from TimPushkin
 - test(crac): add PatchModuleTest for --patch-module compatibility
 - include patch module fds in CRaC check

Changes: https://git.openjdk.org/crac/pull/241/files
  Webrev: https://webrevs.openjdk.org/?repo=crac&pr=241&range=00
  Stats: 168 lines in 5 files changed: 167 ins; 0 del; 1 mod
  Patch: https://git.openjdk.org/crac/pull/241.diff
  Fetch: git fetch https://git.openjdk.org/crac.git pull/241/head:pull/241

PR: https://git.openjdk.org/crac/pull/241


More information about the crac-dev mailing list