垃圾回收算法
标记-清除算法(Mark-Sweep)
- 标记阶段:从根对象开始,遍历所有可达对象并标记它们。
- 清除阶段:扫描整个堆,回收未被标记的对象。
- 优点:简单有效,能够处理对象的循环引用。
- 缺点:清理后会产生内存碎片,可能导致后续的内存分配失败。
标记-整理算法(Mark-Compact)
- 与标记-清除算法类似,但在清除阶段会整理存活对象,将它们移动到堆的一端,并更新引用地址。
- 优点:避免了内存碎片问题,适合老年代。
- 缺点:移动对象需要更新引用,开销较大。
复制算法(Copying)
- 将存活的对象从一块内存区域(源区)复制到另一块内存区域(目标区),清理源区。
- 优点:高效地回收内存,没有内存碎片。
- 缺点:需要分配两块内存,适用于年轻代,且内存利用率较低(通常只用到一半)。
G1(Garbage-First)是一种 面向服务端应用 的垃圾回收器,在 JDK 7u4 版本中正式引入,目的是替代 CMS(Concurrent Mark-Sweep)GC,解决其碎片化问题,并提供可预测的低延迟 GC 机制。
主要特点
✅ Region 分区管理
- 不同于传统的 Eden、Survivor、Old Generation 结构,G1 将整个堆划分为多个等大小的 Region(区域),每个 Region 的默认大小是 1MB ~ 32MB(具体由 JVM 自动调整)。
- Region 的逻辑角色:
- Eden:新生代对象分配区
- Survivor:幸存区(S0、S1)
- Old:老年代
- Humongous:存放超大对象(大于 50% Region 大小的对象)
- Remembered Set(RSet):用于跨 Region 引用管理,避免全堆扫描。
✅ 回收时优先处理垃圾最多的 Region
- G1 采用 “Garbage-First” 策略,即优先回收垃圾最多的 Region,而不是像 CMS 一样全盘扫描整个堆。
- 通过 堆统计 和 预测,最大化吞吐量,并且避免 GC 停顿过长。
✅ 支持并行 & 并发回收
- G1 结合了 并行(Parallel)、并发(Concurrent) 技术,减少 STW(Stop-The-World)暂停时间,提高吞吐量和响应时间。
✅ 可预测的停顿时间目标
- 允许用户设置 最大停顿时间目标(
-XX:MaxGCPauseMillis=200
),G1 会调整回收策略以满足此目标,尽可能控制 STW 时间不超过设定值。
运行过程
G1 主要有 4 种类型的 GC:
- Young GC(年轻代垃圾回收)
- Mixed GC(混合回收,清理年轻代+部分老年代)
- Concurrent Marking(并发标记,标记垃圾对象)
- Full GC(全堆回收,极端情况下发生)
📌 Young GC(年轻代回收)
- 触发条件:Eden 区空间耗尽时触发。
- 过程:
- 停止应用(STW),清理 Eden 区内的垃圾对象。
- 存活对象复制到 Survivor 区(S0 或 S1),如果 Survivor 区满了,部分对象直接晋升到 Old 区。
- 重新分配 Eden 区和 Survivor 区。
📌 Mixed GC(混合回收)
- 触发条件:当老年代空间占用达到
-XX:InitiatingHeapOccupancyPercent
(默认 45%)。 - 回收对象:
- 清理 Eden + Survivor + 部分老年代 Region(优先清理垃圾最多的 Region)。
- G1 通过回收一定数量的老年代 Region 来控制 STW 停顿时间。
📌 Concurrent Marking(并发标记)
- 用于标记堆中垃圾最多的老年代 Region,决定 Mixed GC 需要回收哪些 Region。
- 采用 SATB(Snapshot-At-The-Beginning) 方式进行增量并发标记。
- 过程:
- Initial Mark(初始标记):STW,标记 GC Root 可达对象(时间很短)。
- Concurrent Mark(并发标记):并发扫描存活对象,统计垃圾最多的 Region。
- Remark(重新标记):STW,清理 RSet 记录的跨 Region 引用。
- Cleanup(清理阶段):统计可回收 Region,准备 Mixed GC。
📌 Full GC(全堆回收)
- 触发条件:内存分配失败、Mixed GC 效率低等情况。
- G1 的 Full GC 会回退到单线程 Serial GC,STW 时间较长,应尽量避免。
关键参数
参数 | 作用 |
---|---|
-XX:+UseG1GC | 启用 G1 GC |
-XX:MaxGCPauseMillis=200 | 设定最大 GC 停顿时间(单位 ms) |
-XX:InitiatingHeapOccupancyPercent=45 | Mixed GC 触发阈值(默认 45%) |
-XX:G1HeapRegionSize=8m | 指定 Region 大小 |
-XX:ParallelGCThreads=8 | GC 并行线程数 |
-XX:MaxTenuringThreshold=15 | 控制对象晋升到老年代的年龄 |
优缺点
✅ 优点
- 可预测的 GC 停顿时间,适用于低延迟应用。
- 解决 CMS GC 碎片化问题,避免 Full GC 频繁发生。
- 更好的并发回收能力,减少应用程序停顿时间。
- 自动调整回收目标,适应不同的应用场景。
❌ 缺点
- 比 CMS GC 需要更多的 CPU 资源(因为并发标记和回收)。
- 需要一定的预热时间,G1 GC 需要运行一段时间才能优化停顿时间。
- 不适合小内存(<6GB)应用,G1 适用于 大堆内存(6GB 以上)。
适用场景
- 大内存(6GB 以上)
- 低延迟需求(如电商、游戏、金融系统)
- 避免 Full GC 停顿
⚠ G1 适用于大内存低延迟系统,若追求最大吞吐量,可以考虑 Parallel GC。
总结
- G1 GC 基于 Region 划分内存,避免碎片化问题。
- 采用“Garbage-First”策略,优先清理垃圾最多的 Region。
- 支持 Young GC、Mixed GC、并发标记 GC,减少 STW 时间。
- 可预测 GC 停顿时间,适用于低延迟场景。
- 避免 Full GC,可使用
-XX:MaxGCPauseMillis
控制停顿时间。
🌟 推荐调优参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
-XX:G1HeapRegionSize=8m
-XX:ParallelGCThreads=8
-XX:MaxTenuringThreshold=15
这些参数适用于大部分 Java 低延迟应用,如金融、游戏、电商等系统。
减少 STW 时间的优化策略
在使用 G1 GC 时,减少 STW(Stop-The-World) 时间的关键在于优化 GC 过程,减少一次 GC 所需的工作量,并提高 GC 并发性。以下是几种 减少 STW 时间的优化策略:
调整 MaxGCPauseMillis
控制停顿时间
G1 允许我们通过 -XX:MaxGCPauseMillis=<N>
设置最大可接受的 STW 停顿时间(默认 200ms)。
✅ 调优建议
- 减少 STW 时间:降低
MaxGCPauseMillis
,如-XX:MaxGCPauseMillis=100
- 避免频繁 GC 影响吞吐量:过低的值可能导致 GC 频率过高,可逐步调整 100ms → 200ms → 300ms 观察效果
-XX:MaxGCPauseMillis=100
⚠ 注意:G1 并不能保证严格满足 MaxGCPauseMillis
,但会尽量优化。
适当增加并行 GC 线程数
G1 支持并行 GC 线程数,合理调整 -XX:ParallelGCThreads
可以减少 GC 执行时间。
✅ 调优建议
- CPU 核数 ≤ 8:使用
-XX:ParallelGCThreads=CPU 核心数
- CPU 核数 > 8:使用
-XX:ParallelGCThreads=8 + (CPU 核心数 - 8) / 2
- 例如:
- 16 核 CPU:
-XX:ParallelGCThreads=12
- 32 核 CPU:
-XX:ParallelGCThreads=20
- 16 核 CPU:
-XX:ParallelGCThreads=12
⚠ 注意:
- 并行线程数过高会增加 CPU 竞争,影响应用程序运行。
- 线程数太低,GC 速度变慢,STW 时间变长。
提前触发 Mixed GC,避免 Full GC
Mixed GC 会回收 年轻代 + 部分老年代,避免老年代空间耗尽导致 Full GC(STW 时间极长)。
✅ 调优建议
- 降低
InitiatingHeapOccupancyPercent
,提前触发 Mixed GC- 默认
45%
,可以调低为30%
或35%
- 示例:
-XX:InitiatingHeapOccupancyPercent=35
- 默认
- 避免 Full GC 触发
-XX:G1HeapWastePercent=10
(防止堆碎片过多导致 Full GC)-XX:G1MixedGCCountTarget=8
(每轮 Mixed GC 回收 8 轮)
-XX:InitiatingHeapOccupancyPercent=35
-XX:G1HeapWastePercent=10
-XX:G1MixedGCCountTarget=8
限制 Humongous 对象的影响
Humongous 对象(大于 50% Region 大小的对象)直接分配到老年代,容易触发 Full GC。
✅ 调优建议
- 增大 Region 大小,减少 Humongous 对象分配频率
- 默认 Region 可能较小,导致很多对象变成 Humongous,对老年代压力大。
-XX:G1HeapRegionSize=8M
或16M
适用于大堆内存应用(8GB+)。
-XX:G1HeapRegionSize=8M
- 减少大对象分配
- 避免一次性分配 超大数组,如
byte[]
、String[]
,可以使用 分批分配 或 池化对象。
- 避免一次性分配 超大数组,如
调整 Survivor 区大小,减少对象晋升
Survivor 区太小,导致对象过快晋升到老年代,增加老年代 GC 负担。
✅ 调优建议
- 适当增大 Survivor 区,减少对象晋升
-XX:TargetSurvivorRatio=50
(调整 Survivor 使用率目标,默认 50%)-XX:MaxTenuringThreshold=10
(对象存活 10 次 YGC 才晋升到老年代)
-XX:TargetSurvivorRatio=50
-XX:MaxTenuringThreshold=10
监控 GC 日志,动态调整参数
使用 GC 日志 分析 GC 频率、停顿时间、老年代增长情况,动态调整参数。
✅ 启用 GC 日志
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log
使用 GCEasy、GCViewer 等工具分析 gc.log
,发现 GC 频率、耗时、Full GC 触发原因。
✅ 代码层面优化
- 对象池(减少频繁创建短生命周期对象)
- 使用
StringBuilder
代替String
拼接 - 避免不必要的
soft
/weak
/phantom
引用 - 减少
finalize()
方法(会降低 GC 效率)
✅ 适用大内存应用
如果应用有 16GB+ 内存,可以适当 增大堆大小,减少 GC 频率:
-Xms16g -Xmx16g
总结
优化点 | 方法 |
---|---|
降低 GC 停顿时间 | -XX:MaxGCPauseMillis=100 控制停顿时间 |
增加并行 GC 线程 | -XX:ParallelGCThreads=CPU 核心数 |
提前触发 Mixed GC | -XX:InitiatingHeapOccupancyPercent=35 |
减少 Full GC 触发 | -XX:G1HeapWastePercent=10 避免堆碎片 |
减少 Humongous 对象 | -XX:G1HeapRegionSize=8M |
优化 Survivor 区 | -XX:TargetSurvivorRatio=50 ,减少对象晋升 |
日志分析 GC 状态 | -XX:+PrintGCDetails -Xloggc:gc.log |
通过这些优化,可以 减少 G1 GC 的 STW 停顿时间,提高应用吞吐量和响应速度 🚀。