JDK 16 已经发布。与往常一样,新版本 JDK 会带来一系列新功能、功能增强以及 bug 修复。在这个版本中 ZGC 有 46 个功能增强以及 25 个 bug 修复。这里我会介绍一些更有趣的增强功能。
摘要
-
通过并行线程栈扫描,ZGC 现在暂停时间是微秒级别,平均暂停时间约为 50 微秒(0.05 毫秒),最大暂停时间约为 500 微秒(0.5 毫秒)。暂停时间不受堆、活动集和根集大小的影响。
-
不再有保留堆区域,ZGC 在需要时进行就地移动。这节约了内存,同时也能保证堆在所有情况下都能成功压缩。
-
转发表现在更有效地进行分配和初始化,这缩短了完成 GC 周期所需的时间,特别是在收集稀疏的大型堆时。
亚毫秒级最大暂停时间
(又称并发线程栈处理)
当我们开始开发 ZGC 项目时,我们的目标是让 GC 暂停时间永远不超过 10 毫秒。在当时,10 毫秒似乎是一个很有野心的目标。Hotspot 上提供的其他 GC 算法通常会产生比这更糟糕的最大暂停时间,尤其是在使用大堆时。实现这一目标最重要的是并行处理所有繁重的操作,例如移动(relocation)对象、引用处理以及类卸载。那时候 Hotspot 缺乏并行处理它们所需的基础设施,所以需要花费几年的开发时间实现它们。
在达到最初的 10 毫秒目标后,我们重新确定了一个更具雄心的目标。也就是说,GC 的暂停时间应该永远不超过 1 毫秒。从 JDK 16 开始,我很高兴地向大家报告,我们达到了这个目标。ZGC 现在有着 O(1) 的暂停时间。换句话说,它以恒定的时间执行,并且不随着堆、活动集合(live-set)和根集合(root-set)的大小(和其他内容)增加而增加。当然,我们依然任由操作系统分配 GC 线程的 CPU 时间。但是只要您的系统没有过度配置,您就可以看到 GC 的平均暂停时间约为 0.05 毫秒(50 微秒),最大暂停时间约为 0.5 毫秒(500 微秒)。
所以,我们是怎么做到的?在 JDK 16 之前,ZGC 的暂停时间仍然与根集合的一个子集的大小相关。更准确的说,我们仍然在 Stop-The-World 阶段扫描线程栈。这意味着如果 Java 应用有着大量的线程,那么暂停时间就会增加。如果这些线程有着很深的调用栈,那么暂停时间会增加更多。从 JDK 16 开始