作者:京东科技 文涛
前言
本文所有介绍仅限于HotSpot虚拟机,
本文先介绍了垃圾回收的必要手段,基于这些手段讲解了历代垃圾回收算法是如何工作的,
每一种算法不会讲的特别详细,只为读者从算法角度理解工作原理,从而引出ZGC,方便读者循序渐进地了解。
GC是Garbage Collection的缩写,顾名思义垃圾回收机制,即当需要分配的内存空间不再使用的时候,JVM将调用垃圾回收机制来回收内存空间。
那么JVM的垃圾机制是如何工作的呢?
第一步识别出哪些空间不再使用(识别并标记出哪些对象已死);
第二步回收不再使用空间(清除已死对象 )
判断对象是否已死
判断对象是否已死通常有两种方式 ,引用计数法和可达性分析法
引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不能再被使用的。
简单高效,但无法解决循环引用问题,a=b,b=a
引用计数法并没有在产品级的JVM中得到应用
可达性分析法
这个算法的基本思路就是通过一系列的称为“ GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链( Reference Chain),当一个对象到 GC Roots没有任何引用链相连(用图论的话来说,就是从 GC Roots到这个对象不可达)时,则证明此对象是不可用的。
不过可达性算法中的对象并不是立即死亡的,对象拥有一次自我拯救的机会,对象被系统宣告死亡至少要经历两次标记过程,第一次是经过可达性分析之后没有与GC Roots相连的引用链,第二次是在由虚拟机自动建立的Finalize队列中判断是否需要执行finalize()方法。
HotSopt虚拟机采用该算法。
清除已死对象的方式
标记清除算法
先标记再清除
不足:1 效率问题,标记和清除效率都不高。2 空间问题,产生大量空间碎片
复制算法
内存分两块,A,B
A用完了,将存活对象拷贝到B,A清理掉
代价:内存少了一半。
HotSopt虚拟机用此算法回收新生代。将新生代内存划分为8:1:1的Eden和Survivor解决复制算法内存使用率低的问题
标记整理算法
老年代使用,方式和标记清除类似,只是不直接清除,而是将后续对象向一端移动,并清理掉边界以外的内存。
分代收集算法
分代收集是一个算法方案,整合了以上算法的优点,一般是把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 | |