ZGC是一款在JDK11中新加入的具有实验性质的低延迟垃圾收集器,目前仅支持Linux/x86-64。ZGC收集器是一款基于Region内存布局的,(暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。
ZGC布局与Shenandoah和G1一样,ZGC也采取基于Region的堆内存布局,但与他们不同的是,ZGC的Region具有动态性(动态的创建和销毁,以及动态的区域容量大小)。
ZGC的Region可以分为三类:
小型Region:容量固定为2MB,用于放置小于256KB的小对象。
中型Region:容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。
大型Region:容量不固定,可以动态变化,但必须为2MB的整数倍,用于存放4MB或以上的大对象。并且每个大型Region只会存放一个对象。
ZGC内存布局图:
HotSpot的垃圾收集器,有几种不同的标记实现方案。
把标记直接记录在对象头上(Serial 收集器)。
把标记记录在于对象相互独立的数据结构上(G1、Shenandoah使用了一种相当于堆内存的1/64大小的,称为BitMap的结构来记录标记信息)。
ZGC染色指针直接把标记信息记载引用对象的指针上。
染色指针是一种直接将少量额外的信息存储在指针上的技术。
目前Linux下64位指针的高18位不能用来寻址,但剩余的46位指针所能支持的64TB内存仍然鞥呢够充分满足大型服务器的需要。鉴于此,ZGC将其高4位提取出来存储四个标志信息。
通过这些标志虚拟机就可以直接从指针中看到器引用对象的三色标记状态(Marked0、Marked1)、是否进入了重分配集(是否被移动过——Remapped)、是否只能通过finalize()方法才能被访问到(Finalizable)。由于这些标志位进一步压缩了原本只有46位的地址空寂,导致ZGC能够管理的内存不可以超过4TB。
染色指针示意图:
染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指令向该Region的引用都被修正后才能清理。
染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,设置内存屏障,尤其是在写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护在指针中,显然就可以省去一些专门的记录操作。
染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以便日后进一步提高性能。
内存多重映射Linux/x86-64平台上ZGC使用了多重映射(Multi-Mapping)将多个不同的虚拟内存地址映射到同一物理内存地址上,这是一种多对一映射,意味着ZGC在虚拟内存中看到的地址空寂要比实际的堆内存容量来的更大。把染色指针中的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一物理内裤空间,经过多重映射转换后,就可以使用染色指针正常进行寻址了。
多重映射下的寻址:
ZGC的运作过程大致可划分为以下四个大的阶段。四个阶段都是可以并发执行的,仅是两个阶段中间会存在短暂的停顿小阶段。
运作过程如下:
并发标记(Concurrent Mark): 与G1、Shenandoah一样,并发标记是遍历对象图做可达性分析的阶段,前后也要经过类似于G1、Shenandoah的初始标记、最终标记的短暂停顿,而且这些停顿阶段所做的事情在目标上也是相类似的。
并发预备重分配( Concurrent Prepare for Relocate): 这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set)。