2022年9月5日 作者 zeroheart

可数循环?安全点Safepoint

最近喜欢看掘金,上面看到一篇文章,这里记录下

原文:没有二十年功力,写不出Thread.sleep(0)这一行“看似无用”的代码! – 掘金 (juejin.cn)

是HotSpot虚拟机为了避免安全点过多带来过重的负担,对循环还有一项优化措施,认为循环次数较少的话,执行时间应该也不会太长,所以使用int类型或范围更小的数据类型作为索引值的循环默认是不会被放置安全点的。这种循环被称为可数循环(Counted Loop),相对应地,使用long或者范围更大的数据类型作为索引值的循环就被称为不可数循环(Uncounted Loop),将会被放置安全点。

文中引用了rocketmq的代码,来做例子org.apache.rocketmq.store.logfile.DefaultMappedFile#warmMappedFile

可以看到代码中,使用了Thread.sleep(0)来处理,给的注释是// prevent gc

原文已经解释了,这个地方主要是通过设置safepoint,来让虚拟机在这个过程中不要gc,因为gc不能随便执行,必须在safepoint之后才能保证程序的运行正确。

循环里如果进入safepoint就可以不用等待循环结束才结束gc,减少了gc的时间。

那么是不是Thread.sleep(0)就可以设置safepoint呢?是的,sleep是一个native的方法,进入的时候会有safepoint。

以下是博主的例子

public class MainTest {

    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()+"执行结束!");
        };

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println("num = " + num);
    }
}

正常应该先输出num的值的,怎么改,循环变量改成long就行,会加上safepoint

另外。

这里的4k大小,是可以在ssd的优化技巧。

https://link.juejin.cn/?target=https%3A%2F%2Ftianchi.aliyun.com%2Fforum%2FpostDetail%3Fspm%3D5176.12586969.0.0.13714154spKjib%26postId%3D300892