使用 int 与 long 的 Java For 循环

标签 java jvm garbage-collection

上下文

我正在阅读一份技术文档,它基本上告诉我在 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 无法在循环运行时到达安全点。

要从循环中消除安全点轮询,必须满足两个条件:

  1. 循环是计数的,即具有由整数计数器变量确保的有限迭代次数,并且
  2. -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/

相关文章:

java - 如何给出文件的相对路径?

java - 使用自定义 Mongo 编解码器将文档解码为 Java 类

java - 如何修复 java.lang.UnsupportedClassVersionError : Unsupported major. 次要版本

java - Managed Runtime Initiative 的内核补丁和 JVM 中到底有什么?

java - 合成 lambda 类的神奇类卸载?

Java 将 else { if {} } 转换为 elseif {}

java - Swing 中的 JTable 和数据库

java - JVM getObjectSize 示例

c# - 如何在 C# 中限制线程 RAM 使用

c# - 具有静态成员的未引用对象的 .NET GC