G1 垃圾回收的阶段
本文禁止转载。
鉴于网络上关于 G1 垃圾回收器的回收过程描述不一,经常出现对于回收过程的过度简化,本文旨在明确说明 G1 垃圾回收器的回收过程,主要依据是官方文档,开发人员博客等内容。相信通过对回收过程的了解,可以高效地进行调优,而不只是应对面试。
注:本文不会对于某些概念进行解释,对于具体实现细节也不会过多涉及。你应该先了解 记忆集 Remembered Set、Collection Set(CSset)、SATB 算法、三色标记法、写屏障、region 划分等概念或实现。
G1 中的 GC
Young GC 和 Old GC, 其中 Old GC 又叫做 Mixed GC,同时回收新生代和老年代。G1 中不提供 Full GC 的实现,由其他垃圾回收器实现,不当的配置会导致 Full GC 的出现。Young GC 使用标记复制算法,Mixed GC 使用标记整理算法。
GC 的时机
随着程序启动,Young GC 的进行过程中,会不断将一部分对象复制到 S 区和 O 区,老年代对象逐步增多,整体内存占用逐步增多。YOUNG GC 和 Mixed GC 各自有不同的阶段,当内存占用达到 45%时(-XX: InitiatingHeapOccupancyPercent 可配置),开始全局并发标记(global concurrent marking),之后会进行 Mixed GC。
简单来说,G1GC 分成标记、回收两个阶段。Young GC 相比于 old GC 的频率很高。
以下为 Oracle 关于回收阶段的示意图:
从图中可以看出:并发标记开始到 MixedGC 开始,经过了多次 Young GC。根据回收区域的不同,G1 分成了只回收新生代和混合回收两个阶段,两个阶段交替循环。
Young GC 阶段
- Root Scanning 根扫描
- Update RSet 更新记忆集
- Process Rest 处理记忆集
- Object Copy 对象拷贝
- Reference Processing 处理引用
根扫描中的根包括栈上引用、静态引用。
更新记忆集:处理脏表,更新 RSet
处理记忆集:找到老年代对于新生代的引用。
对象拷贝包括 1. 遍历引用 2. 拷贝存活对象到 S 区或 O 区
处理引用指的是处理软、弱、虚、终结器引用。
并发标记循环(Concurrent Marking Cycle)
内存占用达到阈值时,触发并发标记循环,标记成功后会进行 Mixed GC
- Initial mark phase 初始标记(STW)
- Root region scanning phase 根区域扫描
- Concurrent marking phase 并发标记
- Remark phase 重新标记(STW)
- Cleanup phase 清理(STW)
初始标记:基于 Young GC, 使用 Young GC 的标记结果。
根区域扫描:基于上一步得到的新生代标记对象,标记指向老年代的引用。早于下一次 Young GC 的执行。
并发标记:并发标记所有可达对象,也就是存活对象。
重新标记:完成标记,处理完成 SATB 队列,处理引用(非强引用)。
清理:确定需要清理的 region,区分完全是垃圾的 region, 没有垃圾的 region(可以设定阈值-XX: G1HeapWastePercent = 5,如不超过 5%,无需清理),垃圾与存活对象共存的区域。对于完全是垃圾的区域可以并发清理。使用标记信息重建 RSet。
Mixed GC
当并发标记成功后,回收新生代和部分老年代(默认 1/8,-XX: G1MixedGCCountTarget = 8)。多次 Mixed GC 后,回收完标记的老年代垃圾,开始只进行 Young GC 的阶段。
更详细的并发标记过程
以上图片摘自 G1 开发负责人的博客,分析一下图中的具体实现和之前提到并发标记循环的差别:
Concurrent Clear Claimed Marks 开始了对类对象和类加载器的卸载过程。并发标记过程分成了三个部分,其中 preclean 阶段对于非强引用进行预处理。再标记过程可能重新进入并发标记,如图中虚线所示。
总结
- Young GC 和 Mixed GC 均考虑了跨代引用问题,一个使用的是 RSet, 一个使用的是 Young GC 标记的结果。
- 通过了解垃圾回收的不同阶段,可以理解相关参数调优,保证垃圾回收的速度大于产生的速度,避免 Full GC。
- G1 在不断优化中,其实现随着 JDK 版本不同也有所差异,回收阶段有细微差异。