JVM G1

垃圾回收算法

标记-清除算法(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:

  1. Young GC(年轻代垃圾回收)
  2. Mixed GC(混合回收,清理年轻代+部分老年代)
  3. Concurrent Marking(并发标记,标记垃圾对象)
  4. Full GC(全堆回收,极端情况下发生)

📌 Young GC(年轻代回收)

  • 触发条件:Eden 区空间耗尽时触发。
  • 过程:
    1. 停止应用(STW),清理 Eden 区内的垃圾对象。
    2. 存活对象复制到 Survivor 区(S0 或 S1),如果 Survivor 区满了,部分对象直接晋升到 Old 区。
    3. 重新分配 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) 方式进行增量并发标记。
  • 过程:
    1. Initial Mark(初始标记):STW,标记 GC Root 可达对象(时间很短)。
    2. Concurrent Mark(并发标记):并发扫描存活对象,统计垃圾最多的 Region。
    3. Remark(重新标记):STW,清理 RSet 记录的跨 Region 引用。
    4. 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=45Mixed GC 触发阈值(默认 45%)
-XX:G1HeapRegionSize=8m指定 Region 大小
-XX:ParallelGCThreads=8GC 并行线程数
-XX:MaxTenuringThreshold=15控制对象晋升到老年代的年龄

优缺点

✅ 优点

  1. 可预测的 GC 停顿时间,适用于低延迟应用。
  2. 解决 CMS GC 碎片化问题,避免 Full GC 频繁发生。
  3. 更好的并发回收能力,减少应用程序停顿时间。
  4. 自动调整回收目标,适应不同的应用场景

❌ 缺点

  1. 比 CMS GC 需要更多的 CPU 资源(因为并发标记和回收)。
  2. 需要一定的预热时间,G1 GC 需要运行一段时间才能优化停顿时间。
  3. 不适合小内存(<6GB)应用,G1 适用于 大堆内存(6GB 以上)

适用场景

  • 大内存(6GB 以上)
  • 低延迟需求(如电商、游戏、金融系统)
  • 避免 Full GC 停顿

⚠ G1 适用于大内存低延迟系统,若追求最大吞吐量,可以考虑 Parallel GC。

总结

  1. G1 GC 基于 Region 划分内存,避免碎片化问题
  2. 采用“Garbage-First”策略,优先清理垃圾最多的 Region
  3. 支持 Young GC、Mixed GC、并发标记 GC,减少 STW 时间
  4. 可预测 GC 停顿时间,适用于低延迟场景
  5. 避免 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
-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=8M16M 适用于大堆内存应用(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

使用 GCEasyGCViewer 等工具分析 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 停顿时间,提高应用吞吐量和响应速度 🚀。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值