【博客大赛】JVM深入探索之初探ZGC的领域

【摘要】 背景概要虽然每一门编程语言都会有属于自己的生命周期,有巅峰期同样也有衰退期,Java语言为了保证的巅峰状态一直在线,不断的升华和创新自己特性和能力,经历了从1995年到2020年这么多年的成长,Java技术体系亦愈渐强大,无论是Java虚拟机不断的升级优化还是Java语法糖的不断完善,每一次发布都是一个非同一般的里程碑节点,要说Java体系中最重要的“大脑”,那非JVM虚拟机莫…

背景概要

虽然每一门编程语言都会有属于自己的生命周期,有巅峰期同样也有衰退期,Java语言为了保证的巅峰状态一直在线,不断的升华和创新自己特性和能力,经历了从1995年到2020年这么多年的成长,Java技术体系亦愈渐强大,无论是Java虚拟机不断的升级优化还是Java语法糖的不断完善,每一次发布都是一个非同一般的里程碑节点,要说Java体系中最重要的“大脑”,那非JVM虚拟机莫属了,而JVM中最重要的核心部分就是GC管理子系统。目前,最新颖且最优秀的GC回收器就是ZGC,对于ZGC来讲它的算法结构和设计思想非常值得大家学习和借鉴,无论你对JVM了解程度如何,相信本篇文章都会帮助对ZGC的特性和原理有一个非常深刻且扎实的认识。

基础概念

ZGC的官方定义是:A Scalable Low Latency Garbage Collector“。翻译成中文含义就是一个可扩展的低延迟垃圾收集器。

是Oracle 内部研发的一款新的垃圾回收器,最大的宣传点是低延迟(Low Latency)。根据近期的消息,该项目将会托管给 OpenJDK,由后者开发完善。体现出:可扩展、低延迟

实现目标

                                 ZGC特性                                          备注
                        能够支持TB级别的堆内存回收能力                                          
                          10ms的最大回收停顿时间
                   未来回收的系统运作机制奠定了基础
                   与G1相比较,吞吐率下降的不多于15%
                暂定时间不会随着堆活跃的对象区域的大小提升而变长

性能对比

在SPECjbb这个基准测试中,被测产品要运行JVM中包含了并行机制GC回收器(Parallel Scavenge/Parallel Old),ZGC回收器、G1回收器三者的比较。

比较的环境的CPU模型名称:Oracle Linux 7.4 – Intel Xeon ES-2690 -2.9GHz

例如:Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz或者QEMU Virtual CPU version (cpu64-rhel6)应该能大概看得出是虚拟CPU仍是真实的CPU。

   CPU模型名称         堆内存       操作系统(OS)       物理CPU信息          主频   物理内核    逻辑内核
    Composite          128G      Oracle Linux 7.4    Intel Xeon ES-2690        2.9GHz        16        32

SPECjbb®2015 – Score

image.png

比较的维度:

max-JOPS(Throughput):吞吐率

               ZGC                  Parallel                                         G1
                98%                    100%                                      95%

critical-JOPS(Throughput): 延迟角度的吞吐率

               ZGC                  Parallel                                         G1
                76%                    51%                                      49%

结论

总体分布数据为越高越好。

max-JOPS吞吐率:Parallel=100%,ZGC=98%,G1=95%

critical-JOPS延迟角度吞吐率ZGC=76%,Parallel=51%,G1=49%

总体来讲ZGC的综合性能最好,仅次于Parallel,最后是G1

SPECjbb®2015 – Pause Times

image.png

比较的维度:

ZGC

Linear Scale:线性指标

            Average              95%               99%              99.9%               Max
   1.091ms (+/-0.215ms)        1.380ms        1.512ms             1.663ms       1.681ms

Logarithmic scale :逻辑指标

            Average              95%               99%              99.9%               Max
             0.18MS       0.5MS        0.75MS             5ms       6.1Ms

而对于其他GC回收器来讲,可以看出延迟时间大大增加,此处就不进行数据列举,参考上图可以分析全部远远>10ms之上。

结论

越低越好

各种SPEC®基准测试和内部工作负载进行了特别的性能测量。一般情况下,ZGC能够维护个位数的毫秒暂停时间。

功能特性

  1. 读屏障Load barriers

  2. 染色指针(Colored pointers

  3. 单代(不再区分新生代和老年代)Single generation

  4. 局部压缩(Partial compaction

  5. 基于区域(Region-based

  6. 即时内存重用Immediate memory reuse

  7. NUMA机制(NUMA-aware

  8. 并发机制(Concurrent


总结成为一句话就是:

ZGC是一个并发的、单代(不再区分新生代和老年代)的、基于region的、支持numa的压缩收集器。Stop-the-world阶段仅限于根扫描,所以GC暂停时间不会随着堆或存活对象的多少而增加。

回收阶段

ZGC的一个核心设计原则是结合使用内存屏障和染色对象指针

image.png

ZGC的垃圾回收算法和传统的 Stop-The-World 式的垃圾回收算法不太一样,后者的标记阶段和内存压缩阶段会使得应用线程挂起。ZGC 和 C4(Continuously Concurrent Compacting Collector))算法比较类似。

垃圾回收过程主要分为以下三个阶段:

上图整体是GC的回收过程的周期阶段展示,主要包括了并行化处理阶段:

  • 标记(Marking);
  • 重定位(Relocation)/压缩(Compaction);
  • 重新分配集的选择(Relocation set selection);
  • 引用处理(Reference processing);
  • 弱引用的清理(WeakRefs Cleaning);
  • 字符串常量池(String Table)和符号表(Symbol Table)的清理;
  • 类卸载(Class unloading)。

ZGC能够在运行Java应用程序线程时执行并发操作,比如对象重定位。从Java线程的角度来看,在Java对象中加载引用字段的行为受到内存屏障的限制。

一次完整的 ZGC 回收周期分为以下几个阶段(Phase):

  1. Pause Mark Start:标记根对象;
  2. Concurrent Mark:并发标记阶段;
  3. Concurrent Relocate:并发重定位;

  • 活动对象被移动到了一个新的 Heap Region B-region 中,之前旧对象所在的 Heap Region A-region 即可复用;如果 B-region 中对象之间的引用关系将会在这一阶段被更新;
  • 在重定位过程中,新旧对象的映射关系(同一对象在不同 Region 中的映射关系)被记录在了 Forwarding Tables 中。

Pause Mark Start:这个阶段实际上已经进入了新的ZGC Cycle,同样也是标记根对象;

Concurrent Relocate……

从上面的垃圾回收过程可以看到,正是因为ZGC回收过程中各个 Phase 的并发性,才使得GC Pause不受垃圾回收周期内堆上活动数据数量和需要跟踪与更新的引用数量的影响,将暂停时间保持在较低的水平。

Pause Mark Start

ZGC 的垃圾回收各阶段也不都是并发执行的,在Pause Mark Start 阶段进行根对象扫描(Root Scanning)时会出现短暂STW的暂停。

image.png

ZGC 在Pause Mark Start阶段进行根对象扫描(Root Scanning)建立起到达性分析的对象之间的引用关系图谱。

image.png

Concurrent Remap并发重映射这个阶段除了标记根对象直接引用的对象外,还会根据上个ZGC Cycle中生成的Forwarding Tables更新跨Heap Region的引用;

Concurrent Mark:并发标记阶段,此阶段不会进行stw机制,会处于并发标记与根节点有关系并且可到达的对象。


image.png

同步检查点(Sync point):清理弱引用的数据信息

image.png

活动对象被移动到了一个新的Heap RegionB-region 中,之前旧对象所在的Heap RegionA-region 即可复用;如果 B-region 中对象之间的引用关系将会在这一阶段被更新;

在重定位过程中,新旧对象的映射关系(同一对象在不同Region中的映射关系)被记录在了Forwarding Tables中。

image.png

这允许我们在移动对象/整理内存阶段,在指向可回收/重用区域的指针确定之前回收/重用这部分内存,这有助于降低堆开销。这还意味着不需要实现单独的标记压缩算法来处理完整的GC。

image.png

这允许我们使用相对较少且简单的GC屏障。这有助于降低运行时开销。这还意味着在解释器和JIT编译器中更容易实现、优化和维护GC barrier代码。

局限性

ZGC的初始实验版本将不支持类卸载。默认情况下,classunload和ClassUnloadingWithConcurrentMark选项将被禁用。即便你启用也是不生效的。此外,ZGC最初不支持JVMCI(即Graal)。如果启用EnableJVMCI选项,将打印一条错误消息。

构建和使用

按照惯例,构建系统默认禁用JVM中的实验性特性。ZGC是一个实验性特性,因此不会出现在JDK构建中,除非在编译时使用configure选项:

–with-jvm-features=zgc //显式地启用它。

(ZGC将出现在Oracle发布的所有Linux/x64 JDK版本中)

JVM中的实验特性还需要在运行时显式地解锁。因此,要启用/使用ZGC,需要以下JVM选项:

-XX:+ unlockexperimental alvmoptions -XX:+UseZGC

其他实现低延迟的方案

  • 一种显而易见的方法是让G1的内存整理阶段可以并发执行。这种方案在很早的阶段就被放弃掉了。原因是现有的G1代码并不是为这种设计而编写的,因此要想在保证稳定的前提下实现这种特性非常困难。
  • 一种方案是对现有的CMS收集器进行改进。然而这并不是一种好的选择,原因有很多,比如CMS不支持内存整理、代码复杂性和CMS实际上已经停止开发等。
  • Shenandoah Project正在探索通过使用Brooks Pointers来实现并发。

ZGC 总结

ZGC 在内存整理和引用更新上采取了不同的策略,给垃圾回收过程带来了巨大的性能提升。内存整理和引用更新都是并发的,也是交替进行的(其他的垃圾回收算法在更新引用时需要所有的线程到达safe-point)。但与此同时,我们也应该看到,并发带来的 GC 吞吐率的下降也是不可忽视的。

当响应时间比吞吐量占有更高的优先级时,ZGC 是个不错的选择。而对那些不能接受长时间暂停的应用程序来说,ZGC 是个理想的选择。而对于那些只是在后台进行密集计算的应用程序,G1 或者 Parallel 垃圾回收器可能具有更好的垃圾回收性能。

未完待续……

文章来源: blog.51cto.com,作者:李浩宇Alex,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.51cto.com/alex4dream/2738057

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享