理论部分就不细讲了,具体可以看《 jvm 虚拟机原理》,下面直接画图来演示 ZGC 垃圾回收过程。
第一步 初始状态,视图由 mark0 切换为 Remapped
,其中,大方块是 region,小方块是对象,小方块上面数字是对象地址,下面的是该对象引用的其他对象的地址,r-4代表 remmaped 视图下地址为4,其他同理,m0代表mark0视图,m1代表mark1视图。
第二步 初始标记,视图切换为 mark1,STW
这一步只标记根集合的引用。
第三步 并发标记
这一步标记整个堆,并完成并发重映射。
第四步 最终标记,STW
STW,最终处理并发标记过程中,应用程序新的对象引用变更。
第五步 初始重分配,视图切换为 Remapped,STW
初始重分配后,根集合里的对象引用视图被更新为 Remapped,引用对象分配到新region的,也更新为重分配后的地址。
第六步 并发重分配
其中,并发重分配期间,访问到旧对象,旧指针是可以自愈的。
例如,当访问对象1的属性对象4时,此时对象1里的引用是m1-4,首先会获取m1-4的视图为mark1,然后和当前视图Remapped比较,发现不相等,于是查询对象地址转移信息表,查到了4的新地址,于是将对对象4的引用由m1-4更新为r-4。
如果在对象地址转移信息表里没查到,表示对象没有被重分配,直接更新引用视图为Remapped,如对象3对对象7的引用。
第七步 再次回到第二、三、四步,初始标记、并发标记、最终标记,视图切换为 mark0。
其实第七步应该是并发重映射,但是并发重映射需要扫描整个堆,耗费量比较大,考虑到标记阶段也会扫描整个堆,于是就和标记阶段合并了。
那么标记阶段是怎么体现并发重映射的呢?
我们对比第一步初始状态和第四步最终标记后的状态,我们发现初始状态里,对象引用既有 Remapped,也有 mark0;最终标记后,所有对象引用视图都变成了 mark1,并且将所有引用指针都更新为了最新的。