上下文
我正在阅读一份技术文档,它基本上告诉我在 fori 循环中使用 int 与 long 之间的区别: 场景A(int fori loop)
public class SafePoint {
public static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable runnable=()->{
for (int i = 0; i < 1000000000; i++) {
num.getAndAdd(1);
}
System.out.println(Thread.currentThread().getName()+"end!");
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
}
}
预期结果:
线程 1 结束!
线程0结束!
数 = 2000000000
场景B(长fori循环)
public class SafePoint {
public static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable runnable=()->{
for (long i = 0; i < 1000000000; i++) {
num.getAndAdd(1);
}
System.out.println(Thread.currentThread().getName()+"end!");
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
}
}
预期结果:
num = 55406251(或某个小于 2000000000 的随机数)
线程1结束!
线程0结束!
他给出的最重要的概念是安全点: 由于未计数循环,我们在长 fori 循环中有安全点,但没有 int fori 循环。因此在int fori loop中,sleep需要等待两个线程执行完毕再进行GC,因为线程还在int fori loop时没有安全点。
问题:
我采纳了他的想法并尝试在我的本地机器上重现它们,但失败了: 基本上不管我用int还是long,都是和第二个差不多的结果。先打印数字。
然后仔细想想,只能是我使用的JVM:java 11 corretto的缘故。
根据技术文档的想法,这基本上意味着在 Java 11 中安全点同时存在于计数循环和未计数循环中
问题:
任何人都可以在 java 8 上测试并告诉我这是否是原因?
我实际上已经测试过:在java 8中我们可以观察到A和B的预期结果
Java 11 是否改变了我们放置安全点的方式?为什么?
相关链接:
技术文档试图解释以及如何在 fori 循环中执行 GC:Why Thread.sleep(0) can prevent gc in rocketmq?
最佳答案
延迟println
的原因在this answer中解释。 .简而言之,HotSpot JIT 消除了计数循环内的安全点轮询,因此 JVM 无法在循环运行时到达安全点。
要从循环中消除安全点轮询,必须满足两个条件:
- 循环是计数的,即具有由整数计数器变量确保的有限迭代次数,并且
-XX:UseCountedLoopSafepoints
选项被禁用。
这些条件取决于 JVM 版本和命令行标志。
在 JDK 8 中,int
变量的 for 循环被认为是计数,而 long
变量不是。 UseCountedLoopSafepoints
始终关闭,除非使用 -XX:+UseCountedLoopSafepoints
明确启用。
自 JDK 10 以来,JIT 编译器获得了 Loop Strip Mining特征。此优化显着减少了紧密循环中安全点轮询的开销,因此,-XX:+UseCountedLoopSafepoints
默认启用1。这就是为什么您没有观察到 JDK 11 中的延迟。
1 除非用户选择并行或串行 GC。由于这些垃圾收集器并不关注低暂停,因此 JVM 更喜欢吞吐量,因此不会启用 UseCountedLoopSafepoints
。它仍然可以通过 -XX:+UseCountedLoopSafepoints
手动启用。
关于使用 int 与 long 的 Java For 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73651465/