关于Safepoint与-XX:GuaranteedSafepointInterval的技术疑问
环境信息
- JDK:temurin-1.8.0_332
- 系统:macOS Big Sur
- 虚拟机:HotSpot
GuaranteedSafepointInterval默认值:1000ms
问题1:GuaranteedSafepointInterval=1000ms的定时是否精准?
测试代码
public class Test { public static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { Runnable task = () -> { for (int i = 0;i < 1000000000;i++){ count.getAndAdd(1); } System.out.println(Thread.currentThread().getName() + " done" ); }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); Thread.sleep(1000); System.out.println("count result = " + count); } }
现象与疑问
主线程sleep(1000ms)时,结果符合Safepoint预期;但将sleep时间改为800ms(或小于800ms)时,结果出现异常,因此质疑该参数的定时精准性。
问题2:计数循环Safepoint优化为何未生效?
场景描述
保持主线程sleep(1000ms),将循环内的count.getAndAdd(1)改为count.getAndAdd(i),此时count result会在1秒后才打印。已知HotSpot VM(JDK1.8)对计数循环设置Safepoint时有优化,为何该场景下优化未生效?
解答
针对问题1:GuaranteedSafepointInterval的定时精准性问题
GuaranteedSafepointInterval是兜底触发Safepoint的机制,它的定时并非绝对精准,核心原因如下:
- 触发逻辑是“兜底”:HotSpot会在垃圾回收、JIT编译、线程暂停请求等场景主动触发Safepoint,只有当这些主动触发场景在设定间隔内未发生时,才会由定时器触发兜底Safepoint。
- 线程到达Safepoint存在延迟:当Safepoint请求触发后,VM需要等待所有运行线程到达最近的Safepoint位置。测试线程执行的
getAndAdd(1)是native方法,本身会进入Safepoint,但如果主线程sleep(800ms)后立即打印,此时兜底Safepoint可能尚未触发,测试线程仍在循环执行,主线程读取的count是未同步的中间状态,表现为“结果异常”。 - 系统调度存在误差:操作系统的定时器精度、线程调度延迟会导致1000ms的间隔存在几十毫秒的偏差,这是正常现象。
针对问题2:计数循环Safepoint优化未生效的原因
HotSpot JDK1.8对计数循环的Safepoint优化,仅针对无副作用、循环变量可被JIT完全分析的场景,你的场景优化失效的原因是:
- 循环内调用了
AtomicInteger.getAndAdd(i),这是volatile操作,会触发内存屏障,JIT编译器会判定该方法存在副作用(修改共享变量、调用native代码),因此会强制在每次循环迭代末尾插入Safepoint检查,无法进行优化。 - 方法参数是循环变量
i,每次迭代值都变化,JIT无法对该方法调用做常量折叠或进一步优化,循环也无法被识别为“可优化的纯计数循环”。
内容的提问来源于stack exchange,提问作者sssslaver
相关产品推荐
相关产品推荐

