从历代GC算法角度刨析ZGC

时间:2023-02-06 12:06:56

作者:京东科技 文涛

 

前言

本文所有介绍仅限于HotSpot虚拟机,
本文先介绍了垃圾回收的必要手段,基于这些手段讲解了历代垃圾回收算法是如何工作的,
每一种算法不会讲的特别详细,只为读者从算法角度理解工作原理,从而引出ZGC,方便读者循序渐进地了解。

GC是Garbage Collection的缩写,顾名思义垃圾回收机制,即当需要分配的内存空间不再使用的时候,JVM将调用垃圾回收机制来回收内存空间。

那么JVM的垃圾机制是如何工作的呢?

第一步识别出哪些空间不再使用(识别并标记出哪些对象已死);

第二步回收不再使用空间(清除已死对象 )

判断对象是否已死

判断对象是否已死通常有两种方式 ,引用计数法和可达性分析法

引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不能再被使用的。

简单高效,但无法解决循环引用问题,a=b,b=a

引用计数法并没有在产品级的JVM中得到应用

可达性分析法

这个算法的基本思路就是通过一系列的称为“ GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链( Reference Chain),当一个对象到 GC Roots没有任何引用链相连(用图论的话来说,就是从 GC Roots到这个对象不可达)时,则证明此对象是不可用的。

从历代GC算法角度刨析ZGC

不过可达性算法中的对象并不是立即死亡的,对象拥有一次自我拯救的机会,对象被系统宣告死亡至少要经历两次标记过程,第一次是经过可达性分析之后没有与GC Roots相连的引用链,第二次是在由虚拟机自动建立的Finalize队列中判断是否需要执行finalize()方法。

HotSopt虚拟机采用该算法。

清除已死对象的方式

标记清除算法

先标记再清除

不足:1 效率问题,标记和清除效率都不高。2 空间问题,产生大量空间碎片

复制算法

内存分两块,A,B

A用完了,将存活对象拷贝到B,A清理掉

代价:内存少了一半。

HotSopt虚拟机用此算法回收新生代。将新生代内存划分为8:1:1的Eden和Survivor解决复制算法内存使用率低的问题

从历代GC算法角度刨析ZGC

标记整理算法

老年代使用,方式和标记清除类似,只是不直接清除,而是将后续对象向一端移动,并清理掉边界以外的内存。

从历代GC算法角度刨析ZGC

分代收集算法

分代收集是一个算法方案,整合了以上算法的优点,一般是把Java堆分为新生代和老年代,在新生代中,使用复制算法老年代“标记一清理”或者“标记一整理”

历代垃圾收集器简介

通过上文我们了解了怎样识别垃圾,怎样清理垃圾,接下来,讲ZGC之前,我们回顾一下历代垃圾回收是怎样做的,主要是想给读者一种历史的视角,任何技术都不是凭空产生的,更多的是在前人成果之上进行优化整合

我们先看一个历代JDK垃圾收集器对比表格,以下表格着重说明或引出几个问题:

1 CMS从来未被当作默认GC,且已废弃

2 CMS的思想其实部分被ZGC吸收,CMS已死,但他的魂还在

3 JDK11、JDK17为长期迭代版本,项目中应优先使用这两个版本

版本 发布时间 默认收集器 事件
jdk1.3 2000-05-08 serial 
jdk1.4 2004-02-06 ParNew 
jdk1.5/5.0 2004-09-30 Parallel Scavenge/serial CMS登场
jdk1.6/6.0 2006-12-11 Parallel Scavenge/Parallel Old 
dk1.7/7.0 2011-07-28 Parallel Scavenge/Parallel Old G1登场
jdk1.8/8.0 2014-03-18 Parallel Scavenge/Parallel Old 
jdk1.9/9.0 2014-09-8 G1 CMS废弃
jdk10 2018-03-21 G1 
jdk11 2018-09-25 G1 ZGC登场
jdk12 2019-3 G1 Shenandoah
jdk13 2019-9 G1 
jdk14 2020-3 G1 CMS移除
jdk15 2020-9-15 G1 ZGC、Shenandoah转正
jdk16 2021-3-16 G1 
jdk17 2021-09-14 G1 ZGC分代
jdk18 2022-3-22 G1 
jdk19 2022-9-22 G1