JVM 安全点(safepoint)

安全点(safepoint)

官方解释:A point during program execution at which all GC roots are known and all heap object contents are consistent. From a global point of view, all threads must block at a safepoint before the GC can run. (As a special case, threads running JNI code can continue to run, because they use only handles. During a safepoint they must block instead of loading the contents of the handle.) From a local point of view, a safepoint is a distinguished point in a block of code where the executing thread may block for the GC. Most call sites qualify as safepoints. There are strong invariants which hold true at every safepoint, which may be disregarded at non-safepoints. Both compiled Java code and C/C++ code be optimized between safepoints, but less so across safepoints. The JIT compiler emits a GC map at each safepoint. C/C++ code in the VM uses stylized macro-based conventions (e.g., TRAPS) to mark potential safepoints.

Openjdk 的实现位于 openjdk/hotspot/src/share/vm/runtime/safepoint.cpp

什么是 safepoint

safepoint 可以用在不同地方,比如:(以下内容来源 blog

  • GC – 垃圾收集用户线程暂停
  • Code deoptimization – 逆向优化
  • Flushing code cache – 刷新代码缓存
  • Class redefinition (e.g. hot swap or instrumentation) – 热替换
  • Biased lock revocation – 偏向锁撤销
  • Various debug operation (e.g. deadlock check or stacktrace dump) – 死锁检查、堆栈跟踪转储

以最常见的 GC safepoint 为例,需要知道在那个程序位置上,调用栈、寄存器等一些重要的数据区域里什么地方包含了 GC 管理的指针;

从全局角度看,如果要执行 GC,所有线程都必须在安全点阻塞,直到 GC 完成。

从线程角度看,安全点是线程代码块中的一个特殊位置,当线程执行到这些位置时,JVM 当前状态是安全的,如果需要可以在此处暂停线程,进行 GC。即当需要 GC 时,线程会执行到安全点然后再暂停等待 GC。

而 Deoptimization safepoint,如果要执行一次 deoptimization,只需要执行 deoptimization 的线程进入 deoptimization safepoint。

safepoint 在哪

以 Hotspot VM 为例,什么地方会可以成为 safepoint?

safepoint 的信息要占用内存空间,在 safepoint 会生成代码询问 JVM 是否要进入safepoint,也会造成一定的开销,所以要尽量少放置 safepoint。同时如果 safepoint 太少,可能导致程序长时间运行而不进入 safepoint,GC 无法开始。

safepoint 的可能位置有:(以下内容来源 blog,不同 VM 选择不同)

  1. 循环的末尾(防止大循环的时候一直不进入 safepoint,而其他线程在等待它进入 safepoint)

  2. 方法返回前

  3. 调用方法的 call 之后

  4. 抛出异常的位置

safepoint 如何中断线程

当 JVM 需要让线程进入 safepoint 的时候,需要设置一个标志位,线程运行到 safepoint 的时候主动检查这个标志位,如果标志被设置,那么线程暂停挂起自己,如果没有被设置,那么继续执行。Hotspot 使用这种主动式中断。

另外有一种抢先式中断:GC 发生时,中断所有线程,如果发现有线程不在安全点上,就恢复线程让它运行到安全点上(几乎不用这种方案)。

JIT 编译器会在每个 safepoint 生成一些“调试符号信息”,以便 VM 能找到所需的执行状态。并且把 safepoint 检查的操作插入到机器码指令中:test %eax,0x160100 ,表示检查 0x16010 是否可读,如果不可读,则该线程会被挂起等待。

为 GC 生成的符号信息是 OopMap,指出栈上和寄存器里哪里有 GC 管理的指针;

安全区域(safe-region)

可以看作是扩大了范围的安全点。

当某个线程在执行 native 函数的时候,此时该线程在执行 JVM 管理之外的代码,不能对 JVM 的执行状态做任何修改,因而 JVM 要进入 safepoint 不需要关心它,所以也可以把正在执行 native 函数的线程看作已经进入了 safepoint,或者叫做在 safe-region 里。

JVM 外部要对 JVM 执行状态做修改必须要通过 JNI。所有能修改 JVM 执行状态的 JNI 函数在入口处都有 safepoint 检查,一旦 JVM 已经发出通知说此时应该已经到达 safepoint 就会在这些检查的地方停下来把控制权交给JVM。

还有一种情况是当某个线程在执行 native 函数的时候。此时该线程在执行 JVM 管理之外的代码,不能对 JVM 的执行状态做任何修改,因而 JVM 要进入 safepoint 不需要关心它。所以也可以把正在执行 native 函数的线程看作“已经进入了 safepoint”,或者把这种情况叫做“在 safe-region 里”。
JVM 外部要对 JVM 执行状态做修改必须要通过 JNI。所有能修改 JVM 执行状态的 JNI 函数在入口处都有 safepoint 检查,一旦 JVM 已经发出通知说此时应该已经到达 safepoint 就会在这些检查的地方停下来把控制权交给 JVM。

作者:RednaxelaFX
链接:https://www.zhihu.com/question/29268019/answer/43762165
来源:知乎


http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html
https://www.jianshu.com/p/c79c5e02ebe6
https://blog.csdn.net/iter_zc/article/details/41847887
https://www.zhihu.com/question/29268019
http://www.luyixian.cn/news_show_9357.aspx

Tags:

Add a Comment

电子邮件地址不会被公开。 必填项已用*标注