首页
/ OpenJ9 CRIU 单线程模式下的阻塞操作问题分析

OpenJ9 CRIU 单线程模式下的阻塞操作问题分析

2025-06-24 01:26:42作者:幸俭卉

问题背景

在 OpenJ9 项目中,当使用 CRIU(Checkpoint/Restore In Userspace)功能进行 JVM 检查点时,系统会进入单线程模式。在这种模式下,任何阻塞操作都是被严格禁止的,因为它们可能导致检查点过程失败或产生不可预期的行为。

问题现象

在 JDK24 版本的测试中,多个 CRIU 相关测试用例(如 cmdLineTester_criu_nonPortableRestore_Xtrace_tracepoint_3 和 cmdLineTester_criu_nonPortableRestore_0)出现了失败情况。错误日志显示:

Caused by: openj9.internal.criu.JVMCheckpointException: Blocking operation is not allowed in CRIU single thread mode.
    at java.base/jdk.internal.ref.PhantomCleanable.<init>(PhantomCleanable.java:77)
    at java.base/jdk.internal.ref.CleanerImpl$PhantomCleanableRef.<init>(CleanerImpl.java:164)
    at java.base/java.lang.ref.Cleaner.register(Cleaner.java:225)

技术分析

根本原因

问题出现在 Java 的 Cleaner 机制中。当 JVM 尝试创建 PhantomCleanable 对象时,会调用 CleanerImpl.CleanableList.insert() 方法,这是一个同步方法(synchronized)。在 CRIU 单线程模式下,任何可能导致线程阻塞的操作(如获取锁)都是不允许的。

具体调用栈

  1. 安全检查点线程尝试执行检查点操作
  2. 在初始化过程中,需要创建 Lambda 表达式
  3. 创建 CallSite 时触发了 Cleaner 注册
  4. Cleaner.register() 方法尝试获取 CleanableList 的锁
  5. 由于另一个线程(如 Common-Cleaner)可能已经持有该锁,导致安全检查点线程阻塞

技术细节

在 OpenJ9 的实现中,CRIU 单线程模式会:

  1. 暂停所有非检查点线程
  2. 确保检查点线程不会执行任何可能阻塞的操作
  3. 通过 @NotCheckpointSafe 注解标记不安全的方法

然而,在这个案例中,虽然 PhantomCleanable 的构造函数已经被标记为 @NotCheckpointSafe,但问题仍然出现,这表明:

  1. 注解检查可能在错误的时间点执行
  2. 存在竞态条件,在检查完成后但线程暂停前有代码进入不安全区域
  3. 其他线程(如 Common-Cleaner)可能在检查点过程中持有关键锁

解决方案

针对这个问题,开发团队采取了以下措施:

  1. 确保所有可能阻塞的操作都被正确标记为 @NotCheckpointSafe
  2. 检查 Cleaner 机制中所有同步块的线程安全性
  3. 优化检查点安全检查的时机和范围
  4. 确保在单线程模式下不会尝试获取任何可能被其他线程持有的锁

经验总结

这个案例展示了在 JVM 实现中处理检查点/恢复功能时的典型挑战:

  1. 线程同步与单线程模式的冲突需要特别关注
  2. 系统级功能(如垃圾回收)与特殊模式(如 CRIU)的交互可能产生意想不到的问题
  3. 注解驱动的安全检查需要与运行时行为精确配合
  4. 多线程环境下的资源管理需要全面考虑各种执行路径

通过这个问题的分析和解决,OpenJ9 项目在 CRIU 功能的稳定性和可靠性方面又向前迈进了一步,为后续版本提供了宝贵的技术积累。

登录后查看全文
热门项目推荐
相关项目推荐