JVM 垃圾回收器 ZGC简介
学习ZGC,主要通过学习ZGC设计与实现书籍,并以博客的形式记录学习内容
ZGC在JDK11引入,目前被标记为实验性质。ZGC的出现是为了解决G1的不足。
G1不足之处
G1的目标是在可控的停顿时间内完成垃圾回收,所以进行了分区设计,在回收时采用部分回收(在YGC时会回收所有新生代分区,在混合回收时会回收所有的新生代分区和部分老年代分区),支持也可以达到几十GB或者上百个GB。为了进行部分回收,G1实现RSet管理对象的引用关系。基于以上设计的特点,导致G1存在以下的问题:
- 停顿时间过长。G1收集器以由用户指定期望的停顿时间。 不过,这个停顿时间再怎么低也得有个限度,它默认的停顿时间为200ms,但如果我们把停顿时间调得非常低,很可能出现的结果就是由于停顿目标时间太短,导致每次只能回收一小部分, 回收速度跟不上分配速度, 导致垃圾慢慢堆积,最终占满堆引发FGC反而降低性能, 所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的,这个数字已经非常小了,但是在特定的场景下不能满足实际需求。
- 内存利用率不高,通常引用关系的处理需要额外消耗内存,一般占用整个内存的1%~20%。
- 支持的内存空间有限,不适用于超大内存系统,特别是在内存容量高于100GB的系统中,会因为内存过大而导致停顿时间增长。目前从实践测试情况来看,相同环境下,较大内存(超过100GB)会出现更长时间的停顿,内存越大则管理的时间越长。
ZGC设计目标
- 支持TB级内存,目前最大支持4TB堆内存。
- 停顿时间控制在10ms以内,目前停顿时间通常在10ms以下,并且垃圾回收所引起的停顿时间并不会随着内存的增大而延长。
- 对程序吞吐影响小于15%。
ZGC从G1优化部分
ZGC如何设计以达到目标这一问题。简单的说,就是ZGC把一切能并发处理的工作都并发处理执行。
并发并行概念:
- 并行指多个垃圾回收相关线程在操作系统之上并发地运行,强调只有垃圾回收线程工作,Java应用程序都暂停执行,因此并行线程执行的时候一定发生了STW。
- 并发指如果启动了多个线程,那么与垃圾回收相关的线程并发的运行,同时这些线程回合Java应用程序并发的运行。
ZGC比G1优秀部分
- ZGC多个阶段从G1并行执行优化到并发执行。参照上图,G1已经实现了并发标记,所以标记不会再影响停顿时间。G1中的停顿时间主要来自于垃圾回收阶段中的复制算法,在复制算法中,需要把对象转移到新的空间中,并且更新其他对象到这个对象的引用。实际中对象的转移涉及内存的分配和对象成员遍历的复制,而对象成员变量的复制是非常耗时的,通常转移时间占比80%。在G1中对象转移都是在STW中并行执行的,而ZGC是把对象转移采用并发执行,从而满足停顿在10ms以下。
- G1中可能存在FGC,如果发生FGC,也可能导致停顿之间不可控。在目前ZGC中,垃圾回收就是全量回收,也就是发生一次垃圾回收就是一次FGC,而每次垃圾回收的停顿时间在10ms以下,所以FGC导致停顿时间不可控这一问题也解决了。
ZGC特点
- 不分代的垃圾回收器,即垃圾回收时对全量内存进行标记,但是回收时仅针对部分内存回收,优先回收垃圾比较多的页面
- 仅支持Linux64位系统,不支持32位平台
- 不支持使用压缩指针
- 内存分区管理,且支持不同的分区粒度,在ZGC中分区称为页面,有小页面、中页面、大页面。
- 具有颜色指针,通过设计不同的标记位区分不同的虚拟空间,而这些不同的标记位指示的不同的虚拟空间通过mmap映射在同一物理地址;颜色指针能够快速实现并发标记、转移和重定位
- 设置了读屏障,实现并发标记和并发转移的处理
- 支持NUMA,尽量把对象分配在访问速度比较快的地方