👈👈👈 欢迎点赞收藏关注哟
首先分享之前的所有文章 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164...
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca...
一. 前言
G1 回收器的回收循环主要分为3大主要的类型 : 年轻代循环 ,多步骤并行标记循环 ,混合收集阶段。 同时流程外还有一个保护性的 FullGC 同样存在。
而在流程上我会把年轻代再细分一下,来模拟一下当对象逐渐增长情况下,回收的演变。
历史文章 :
先来看张流程图 :
文章目的 :
- 梳理 G1 GC 整个阶段的大流程
- 梳理 每个环节的内存变化
- 不涉及到源码及细节原理
二. 回收的阶段
2.1 第一阶段 : YoungGC (新生代 GC)
- S1 : 初始时会分配给 5% 大小的 Region 给新生代
- S2 : G1 从空闲区间里面挑选出区间加入年轻代
- S3 : 当新生代的体积逐渐变大,
Eden 区已经无法扩展
(60% Region)时,则会触发一次YoungGC
.- 这个阶段还会
计算总量,统计RSet,当前容量
等等数据,用于后续整理年轻代 - 该环节收集的内存是
不固定
的 ,回收的分区数目也是不固定的
- 这个阶段还会
- S4 : 在这个环节会
触发 STW
, 通过复制算法收集 Eden 区存活的对象,放入 S0 Survivor - S5 : 在这个过程中 , 会交替使用 S0 和 S1 两个幸存区,直到对象或者整体容积达到触发老年代的条件
也就是说每次YGC后都会为对象生成年龄信息,放入一个年龄虚拟表
- 每一次 YGC 后都会带来新生代分区数目的变化
在这个阶段中,会对 Eden 和 Survivor 区进行回收,清除垃圾对象,添加对象的生命周期。
- 核心点 :
首先是根集合 ,然后根据根集合处理的对象处理 RSet 的管理关系。
- TODO : 当然这里还涉及到没有回收的区域的引用关系以及具体的引用实现,这一篇就不细说了。
2.2 第二阶段 :进化到老年代
- S1 : 随着
一轮一轮
的 YoungGC , Survivor 的对象越来越多 , 部分对象熬过了多轮 YoungGC - S2 : 此时对
达到了老年代阈值
的对象进行转移,将这部分生命超过MaxTenuringThreshold
的对象回收到老年代 - S3 : 在高并发场景或者回收的继续迭代,Survivor 存活的对象超过了其本身空间的50%
- S4 : 取当前 50% 的年龄对象 ,对大于该年龄的对象进行转移,放入老年代
这其实属于第一阶段,单独做了一层划分,在这阶段老年代对象会不断积累。
阶段总结
由于这个阶段的积累,老年代数据越来越多 ,就会触发混合 GC (Mixed GC ).
这个 GC 阶段分为两个阶段 :
- 并发标记 👉 下文 2.3 第三阶段部分
- 用于识别老年代里面的活跃对象 ,判断垃圾对象所占空间以及是否需要回收
- 垃圾回收 👉 下文 2.4 第四阶段部分
- 流程基本和新生代一致,区别在于会针对老年代的Region一起回收
好了 ,继续看下面的!!
2.3 第三阶段 : 并发标记周期
到了这个环节 , 老年代的对象也逐渐积累,这个时候就要为混合回收做准备了。 在这个过程中 ,不会阻碍应用程序的执行
, 通过 GC Root 对堆中对象进行可达性分析,查找到所有可以回收的对象。
在这个阶段中 ,标记会分为几个阶段 :
初始标记(STW) 👉 根区域标记 👉 并行标记 👉 重新标记(STW) 👉 清理阶段
- ❓ 初始化标记 (
Initial Mark
) :会标记出所有的根对象以及直接与根对象关联的对象。 在这过程中,会触发一次 STW , 从而建立一个 SATB 快照 ,用于后续的跟踪。- 根对象 : 栈对象 ,全局对象 ,JNI 对象等
- 特点 :此阶段会
直接借用
YGC 标记的结果,将 Survivor 分区作为根
- ❓ 并发标记 (
Concurrent Marking
): 当 YGC 触发后 ,如果满足并发标记的条件,则会触发并发标记- InitiatingHeapOccupancyPercent : 默认45 , 即当内存分配超过内存总量的 45% 时 ,触发
- XX:ConcGCThreads : 启动的并发线程的数量 , 每个线程每次只扫描一个分区
- STW : 该阶段会扫描所有的分区,
不需要 STW
, 和主线程并发
- ❓ 重新(最终)标记 (
Final Remark
) : 当 YoungGC 经历了多轮后,就可能会进入 Remark 重新标记阶段,- 目标 : 处理在并发标记期间发生的引用变化,确保标记信息的准确性。
- 行为 : 该阶段保证了根扫描及存活对象扫描以及完成 ,同时
待处理的标记栈为空
- STW : 这个阶段也
会触发STW
,原因在于需要保证引用都被处理(一直在变
)
- ❓ 清理阶段 (
Cleanup
): 在这个阶段会进行选择性的回收- 独占清理 : 基于前面的标记信息来计算各个区域的
存活数量以及可回收比例
,放入排队序列
- 并发清理 : 识别并且清理完全空闲的区域,这个阶段是并发执行的 , 清理完直接放入空闲区间
- 并发整理 : 将存活的对象从多个 Region
复制
到多个连续
的 Region 中,保证空间的连续 - 其他操作 : 包括
重置RSet
,交换标记位图
- 特别注意 : 清理操作并不会清理垃圾对象 !!
- 独占清理 : 基于前面的标记信息来计算各个区域的
这里我直接借用 《JVM G1源码分析和调优 》 来展现整个流程 ,大家可以看看原书,写得很好
- 重点一 : 可以明确的看到 ,并不是并发标记后就不 YGC 了, 在 MixedGC 前会一直有
- 重点二 : 并发标记 和 混合回收整个时间周期其实不短
2.4 第四阶段 : 混合阶段垃圾回收
IHOP (-XX:InitiatingHeapOccupancyPercent) :
堆内存占用阈值,用于触发混合回收,默认 45%。- 不同于 CMS 可以针对老年代设置阈值 , G1 设置的 IHOP 是针对整个堆内存(
Region没有明确的分代
)
混合回收会对新生代 ,老年代 ,以及大对象进行回收
。 另外混合回收会触发 STW , 同时触发最终标记阶段,确定哪些对象是存活对象,哪些对象需要回收,
由于在之前的过程里面,已经对所有区域的所有对象进行过标记,所以可以很轻松的知道那些区域里面的垃圾比例,也就是之前提到的垃圾密度。 G1 回收器会首先收集垃圾比例较高的阶段。
与新生代回收一样,这个阶段剩余的对象会统一合并到多个区域里面去。
这个阶段的操作和 YGC 基本一致,这里就不细说了。
2.5 最终阶段 : FullGC
这个阶段并不是绝对执行,当吞吐量过大或者系统问题导致堆内存不足时,就会触发一次 FullGC.
FullGC 阶段和其他的垃圾回收器一样,也会 STW 同时对所有的区域进行清理。
- S1 : 对象分配失败 ,进入 Evac 阶段
- S2 : 尝试再次分配内存 ,如果分配失败 ,则进入 FullGC
这一块其实很复杂,涉及到 RSet的处理,一篇肯定说不清楚,关键是我也没完全看懂,总结不出来啊!
实在想了解的可以关注或者直接看书。
三. 关于回收过程中的标记
3.1 关于 Survivor 空间
谈到 G1 回收器的幸存者区的时候,通常会提到四个值 : S0 Survivor Space 和 S1 Survivor Space 以及 From Survivor Space , To Survivor Space。
那么他们有什么关系呢 ?
S0 Survivor Space 和 S1 Survivor Space 可以理解为一种物理上的概念 , 当对象创建的时候,如果 Eden 满了触发 YoungGC , 一般收集完成后,把存活的对象放在 S0 区域中。而在下一次垃圾回收的过程中 ,由于 S0 也会被回收, 此时就会把存活的对象放到 S1 区域中。从而实现两个幸存区的交替使用。
而 From 和 To 开头的幸存区则是一种逻辑概念。
- From Survivor Space 是
上一轮
垃圾回收结束后被用作存放存活对象的Survivor区 - To Survivor Space 是
当前
垃圾回收正在使用的Survivor区。
例如当前被回收的是 S0 , 当前空着的是 S1 . 我们会回收完成后把存活对象放在 S1 中。在这个场景下 : S0 = From , S1 = To.
3.2 关于大对象
G1 为大对象引入了一个大对象区间,当一个对象超过一个 Region 的 50% 时 , 就会被认定为大对象。 G1 对于这些大对象不会放在老年代,而是放在 老年代之外
的 大对象区间
里面。
大对象区间通常是多个连续的 Region 区间 。而在回收时,年轻代回收,混合回收, FullGC 都会对大对象区间进行回收工作。
补充的问题 :
就像上文说的并发标记实际上是从 YGC 的结果作为根的 。 当时很多场景下 ,老年代的对象并没有被年轻代的引用
.
这种情况下就会导致对象被漏标 ,所以在这阶段 ,要把直接从根出发到老生代的引用或者大对象分区的引用补上。
总结
这一块勉强算是弄清楚了 ,下一篇就针对其中的一些重点进行梳理了 ,膜拜大佬。
整个系列会很长,包括 G1 , ZGC 等多个系列,欢迎关注。
很多东西看的时候也是一知半解,如果有问题欢迎指出。
参考文档
👍 《JVM G1源码分析和调优》
《深入Java虚拟机:JVM G1GC的算法与实现》
《深入理解JVM & G1 GC》
zhuanlan.zhihu.com/p/405142523