ZGC【Java】
简介
ZGC是自JDK11中开始推出的一款垃圾回收器,ZGC的主要特点是支持大内存(大达16TB),低延迟(低至10ms)。
ZGC的堆内存和G1类似,也是基于 Region 来分布,但是 ZGC 抛弃了分代的概念,不再区分新生代和老年代。而且 ZGC 的 Region 支持动态创建和销毁,ZGC 中的 Region 大小分为三种类型 :
小对象区(Small Region),默认2MB,用于放置小于 256 KB 的对象。
中等对象区(Medium Region),主要用于放置大小在 256 KB 和 4 MB 之间的对象,默认大小是32M。
对于大于32M的对象,会被放入Large Region,Large Region每个Region只会放值一个对象,为了对齐,所以Region的大小必须是2MB的倍数,Large Region的最小内存是4MB。
ZGC使用多重映射和染色指针实现垃圾回收。
原理
ZGC不再区分新生代和老年代,垃圾回收的过程中分为标记阶段和转移阶段。
标记算法
标记过程可以分为初始标记,并发标记和重新标记。
初始标记阶段是STW(Stop The World,暂停所有业务线程)的,因为初始标记阶段是将和GC root直接关联的对象进行标记,所以该阶段需要一个静态的快照,也就是需要STW。
初始标记完成之后,标记工作就可以和业务线程同时进行了。 并行标记的一般采用三色标记的方式进行标记,三色标记是指在标记过程中将对象分为白色,黑色,灰色三种。
颜色 | 定义 |
---|---|
黑色 | 对象自身扫描完成,并且完成了该对象所有引用的标记动作。 |
灰色 | 对象自身扫描完成,对象所引用的对象至少还有一个未标记完成,当标记完成之后变成黑色。
灰色是中间状态,当垃圾回收结束的时候只有白黑两种颜色。 |
白色 | 没有被垃圾回收扫描过,当扫描结束的时候白色对象可以被回收。 |
三色标记的过程如下:
- 初始阶段所有的对象都是白色。
- 初始标记完成之后将GC Roots直接关联的对象置为灰色。
- 遍历灰色对象的所有引用,当所有引用被标记完成之后,灰色对象置为黑色,该对象引用的对象置为黑色,重复该过程,直到所有的对象都变成黑色。
- 当标记过程结束,白色对象可以被回收。
三色标记算法有三类问题,多标问题,漏标问题,以及新对象的问题。
多标问题是指在垃圾回收过程中该对象是扫描到被引用的,但是过程中断开了所有的引用,此时该对象已经是事实的垃圾,但是被标记为黑色,也就是多标问题。
多标问题关联的对象一般称为浮动垃圾,其不影响垃圾回收的正确性,可以在下次垃圾回收的时候进行清理。
漏标问题是指在垃圾回收过程中,该对象被灰色对象引用,但是过程中灰色对象到该对象的引用断开,而同时又有黑色对象引用了该对象,黑色对象因为不会再进行引用对象的标记,该对象就不能被正确扫描,从而被漏标。
漏标问题一般有两种方式解决,原始快照和增量更新。
原始快照是断开灰色对象和其他对象链接的时候,会把该关联关系保存下来,扫描结束之后,使用这条关系的快照重新将该对象进行标记。
增量更新是指当黑色节点指向其他对象的时候,将这个新增的引用关系保存下来,扫描结束的时候,再重新扫描该黑色对象节点。
重新标记阶段主要是为了修正并发标记阶段的漏标问题。该阶段也需要STW。
染色指针
因为对象需要区分是活跃对象还是待回收对象,活跃对象就是有被引用的对象。
如何标识这些对象是否需要清理,不同的GC实现有不同的方式:
- Serial等垃圾收集器把标记记录在对象的头部。
- G1等垃圾收集器使用BitMap记录标记信息,BitMap需要额外的内存空间,一般占堆内存1/64。
- ZGC使用染色指针进行记录标记。
目前的计算机体系结构例如AMD64架构中只支持48位(256TB)的虚拟地址空间,所以对于64位的硬件结构而言,使用低48位的地址就可以满足虚拟地址空间的寻址需求,ZGC的染色指针技术利用64位地址剩余的寻址无关的高位中的4位存储了四个标志位。指针中的这些标志位可以表达三色标记的信息,移动的信息以及是否只能通过finalize()方法才能被访问到。
因为 ZGC 的过程不需要访问对象就可以获取标记信息对GC的效率有较大的提升。
ZGC 的染色指针使用了地址指针来表达信息,其使用了虚拟内存映射技术,把同一个物理内存地址可以映射为 Marked0、Marked1 和 Remapped 三个虚拟内存空间。应用程序创建对象会在堆上申请一个虚拟地址,这时 ZGC 会同时在 Marked0、Marked1 和 Remapped 这三个视图空间为这个对象分别申请一个虚拟地址,这三个虚拟地址同时映射到对象实际的物理空间。
Marked0、Marked1 和 Remapped 作为 ZGC 的三个视图空间,在不同的时间周期内只有一个生效。ZGC 通过这三个视图空间的切换,来进行垃圾标记的记录工作。
在标记阶段开始的时候,全局视图切换到 M0 视图,类似于三色标记开始的时候将所有对象置为白色。
标记的过程中,新生成的对象,应用程序使用的对象,或者GC线程标记的对象都会置为 M0 视图。
在标记结束时,如果对象的地址视图是M0,说明对象是活跃的,反正如果对象的地址视图是Remapped,说明对象是已经不再被引用,可以回收其占用的内存空间。
在转移阶段 ZGC 会切换到 Remapped 视图。将对象地址空间是 M0 的对象进行转移。
因为在上一轮垃圾收集还未结束的时候,可能会启动新一轮的垃圾收集,此时 M0 的对象可能还未被转移完毕,所以 M1 的地址视图用来标识此轮垃圾回收中的对象视图,此时 M0 标识前一次垃圾回收的标记阶段被标记过的活跃对象,对象在转移阶段未被转移,但是在本次垃圾回收中该对象已经不再被引用。
收集算法
ZGC 也使用复制算法进行垃圾回收。