1. JVM介绍和运行流程-CSDN博客
2. 什么是程序计数器-CSDN博客
3. java 堆和 JVM 内存结构-CSDN博客
4. 虚拟机栈-CSDN博客
5. JVM 的方法区-CSDN博客
6. JVM直接内存-CSDN博客
7. JVM类加载器与双亲委派模型-CSDN博客
8. JVM类装载的执行过程-CSDN博客
9. JVM垃圾回收-CSDN博客
10. 垃圾回收的算法-CSDN博客
11. JVM中的分代回收-CSDN博客
12. JVM的垃圾回收器-CSDN博客
13. G1垃圾回收器-CSDN博客
14. 垃圾回收的引用区别-CSDN博客
15. JVM调优的参数设置-CSDN博客
16. JVM调优工具-CSDN博客
1. G1垃圾回收器
G1(Garbage-First)垃圾回收器是Java HotSpot虚拟机中一种面向服务端应用的垃圾回收器,于JDK 7中首次推出,并在JDK 9及以后成为默认的垃圾回收器。
内存布局
-
区域划分:
-
堆被划分为多个大小相等的Region(默认约2048个)
-
每个Region可以是Eden、Survivor或Old区
-
还有一种特殊的Humongous区用于存储大对象(大小超过Region 50%的对象)
-
-
分代管理:
-
仍然保留分代概念,但物理上不再连续
-
年轻代:一组Region(Eden+Survivor)
-
老年代:另一组Region
-
2. G1的三个核心阶段
2.1 年轻代回收(Young GC) —— 一阶段
特点:
-
STW(Stop-The-World):完全暂停应用线程
-
触发条件:Eden区填满时自动触发
-
目标:快速回收年轻代(Eden + Survivor区)
工作流程:
-
根扫描:标记GC Roots直接引用的对象。
-
存活对象标记:通过可达性分析标记年轻代存活对象。
-
复制/晋升:
-
存活对象被复制到Survivor区(复制算法)
-
达到晋升阈值(
-XX:MaxTenuringThreshold
)的对象晋升到老年代。
-
-
区域清空:回收后的Eden区和部分Survivor区被标记为空闲。
关键点:
-
仅处理年轻代Region,耗时短(通常几毫秒到几十毫秒)。
-
通过
-XX:G1NewSizePercent
和-XX:G1MaxNewSizePercent
动态调整年轻代大小。
图示过程:
- 初始时,所有区域都处于空闲状态
- 创建了一些对象,挑出一些空闲区域作为伊甸园区存储这些对象
- 当伊甸园需要垃圾回收时,挑出一个空闲区域作为幸存区,用复制算法复制存活对象,需要暂停用户线程
E 表示:Eden 区
S 表示:Survivor 区
- 随着时间流逝,伊甸园的内存又有不足
- 将伊甸园以及之前幸存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代
2.2 并发标记周期(Concurrent Marking Cycle)—— 二阶段
特点:
-
部分并发:大部分阶段与应用线程并发执行
-
触发条件:老年代占用达到阈值(默认45%,通过
-XX:InitiatingHeapOccupancyPercent
调整) -
目标:全局标记堆中存活对象,确定回收优先级
分阶段流程:
-
初始标记(Initial Mark,STW):
-
短暂暂停,标记GC Roots直接关联的对象。
-
通常与年轻代回收一起执行(借道Young GC)。
-
-
并发标记(Concurrent Mark):
-
与应用线程并发,遍历整个堆,标记所有可达对象。
-
使用SATB(Snapshot-At-The-Beginning)算法处理并发期间的对象变化。
-
-
最终标记(Remark,STW):
-
处理并发标记期间漏标的对象(通过写屏障记录的变化)。
-
引用处理(如清理软引用/弱引用)。
-
-
清理(Cleanup,STW):
-
统计各Region的存活对象比例,排序回收价值(垃圾最多的优先)。
-
不实际回收内存,但可能回收完全空闲的Region。
-
关键点:
-
并发标记周期为后续混合回收提供数据基础。
-
通过
-XX:ConcGCThreads
可调整并发标记线程数。
图示过程:
当老年代占用内存超过阈值(默认是45%)后,触发并发标记,这时无需暂停用户线程
2.3 混合回收(Mixed GC)—— 三阶段
特点:
-
STW:暂停应用线程
-
触发条件:并发标记周期完成后,老年代Region达到回收阈值
-
目标:同时回收年轻代和部分老年代(选择垃圾比例高的Region)
工作流程:
-
年轻代回收:与Young GC相同,处理Eden和Survivor区。
-
老年代回收:
-
根据并发标记的结果,选择垃圾比例最高的老年代Region(Garbage-First策略)。
-
存活对象被复制到其他Region(压缩算法减少碎片)。
-
-
多次增量回收:
-
可能分多次完成老年代回收,每次回收部分Region以控制停顿时间。
-
关键点:
-
通过
-XX:G1MixedGCLiveThresholdPercent
设置Region存活对象阈值(默认85%,存活对象过多则跳过)。 -
通过
-XX:G1MixedGCCountTarget
控制混合回收的批次(默认8次)。
图示过程:
- 并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程。
- 这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。
- 此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高(存活对象少)的区域(这也是 Gabage First 名称的由来)
混合收集阶段中,参与复制的有 eden、survivor、old
复制完成,内存得到释放。进入下一轮的新生代回收、并发标记、混合收集
三阶段关系图
Full GC(后备方案)
异常情况:Full GC
当G1无法满足回收需求(如并发模式失败或晋升失败)时,会退化为单线程的Serial Old GC(Full GC),导致长时间停顿。应通过调整参数(如增加堆大小、降低InitiatingHeapOccupancyPercent
)避免。
3. 问题总结
3.1 三阶段总结
G1 通过这三个阶段的配合,实现了低延迟与高吞吐的平衡,尤其适合大内存、多核CPU的场景。
阶段 | 并发性 | 目标 | 触发条件 |
---|---|---|---|
年轻代回收 | STW | 快速回收Eden/Survivor | Eden区满 |
并发标记周期 | 部分并发 | 标记全堆存活对象,确定优先级 | 老年代占用达阈值(默认45%) |
混合回收 | STW | 回收年轻代+高垃圾老年代Region | 并发标记周期完成后 |
3.2 说一下G1垃圾回收?
- 应用于新生代和老年代,在JDK9之后默认使用G1
- 划分成多个区域,每个区域都可以充当eden,survivor,old,humongous其中 humongous 专为大对象准备
- 采用复制算法
- 响应时间与吞吐量兼顾
- 分成三个阶段:年轻代回收(stw)、并发标记周期(重新标记stw)、混合回收
- 如果并发失败(即回收速度赶不上创建新对象速度),会触发Full GC