当前位置: 首页 > news >正文

G1 垃圾回收器调优

目录
  • 背景和价值
    • 1 不要设置 -xmn来指定新生代堆内存,这种做法存在因为堆内存预估失败而导致的响应时间激增的问题。
    • 5 停顿时间。-XX:MaxGCPauseMillis 默认200ms,对响应时间敏感的应用(如 Web 应用、金融交易系统)可设置更低的值
    • 6 Region大小设置。较大的 Region 可以减少内存碎片,但可能会增加垃圾回收的粒度。
    • 7 设置 Mixed GC 的阈值,如果老年代占用堆内存的比例超过该阈值,G1 会启动 Mixed GC。如果应用有大量长期存活对象,可以适当降低该值。
    • -XX:InitiatingHeapOccupancyPercent
    • -XX:+UseStringDeduplication
  • G1垃圾回收生命周期
      • 1. 年轻代回收(Young GC)
      • 2. 并发标记周期(Concurrent Marking Cycle)
      • 3. 混合回收(Mixed GC)
      • 4. 完全垃圾回收(Full GC)
      • 生命周期流程图
      • 关键注意事项
  • 参考资料

背景和价值

1 不要设置 -xmn来指定新生代堆内存,这种做法存在因为堆内存预估失败而导致的响应时间激增的问题。

-XX:G1NewSizePercent(新生代占用堆内存的最小比,默认为5%)以及-XX:G1MaxNewSizePercent(新生代占用堆内存的最大比,默认为60%)
新时代老年代占比。默认新时代占60%,如果应用有大量短期对象,可以适当增加年轻代的大小。

5 停顿时间。-XX:MaxGCPauseMillis 默认200ms,对响应时间敏感的应用(如 Web 应用、金融交易系统)可设置更低的值

6 Region大小设置。较大的 Region 可以减少内存碎片,但可能会增加垃圾回收的粒度。

6.1 堆内存大小与 Region 大小的关系。如果堆内存较小,Region 大小应该相应减小;如果堆内存较大,Region 大小可以适当增大。
6.2 如果应用程序中经常会产生大对象(如大于 Region 大小一半的对象),需要确保 Region 大小能够容纳这些大对象。否则,G1 可能需要使用多个 Region 来存放一个大对象,这会增加内存管理的复杂性和垃圾回收的难度。

7 设置 Mixed GC 的阈值,如果老年代占用堆内存的比例超过该阈值,G1 会启动 Mixed GC。如果应用有大量长期存活对象,可以适当降低该值。

Mixed GC 负责回收老年代 Region

Mixed GC 负责回收老年代 Region,关键参数:

  • -XX:G1MixedGCLiveThresholdPercent
    仅回收存活对象比例低于此值的 Region(默认 85%):
    -XX:G1MixedGCLiveThresholdPercent=80
    
  • -XX:G1HeapWastePercent
    允许的堆浪费比例,达到该值会触发 Mixed GC(默认 5%):
    -XX:G1HeapWastePercent=10
    
  • -XX:G1MixedGCCountTarget
    单次 Mixed GC 的回收轮次(默认 8),增大此值可减少单次停顿时间:
    -XX:G1MixedGCCountTarget=16
    

-XX:InitiatingHeapOccupancyPercent

触发并发标记周期的堆占用阈值(默认 45%)。如果 Full GC 频繁,可适当降低该值:
避免Full GC,因为G1的设计目标之一就是减少Full GC的发生。这时候需要确保堆足够大,或者调整InitiatingHeapOccupancyPercent,避免过早触发并发周期。

-XX:InitiatingHeapOccupancyPercent=35
  • 当整个堆占用超过某个百分比时,就会触发并发GC周期,这个百分比默认是45%,我的理解来说,如果你的项目没有大的cpu负载压力,可以适当降低这个值,带来的好处就是提前开始Concurrent Marking Cycle Phases ,进一步来说,回收 年轻代 and 老年代 也会提前开始,这样有利于防止年轻代晋升老年代失败(老年代容量不足)而触发Full GC。

经过观察发现如果这个数值设定过大会导致JVM无法启动并发标记,直接进行FullGC处理。G1的FullGC是单线程,一个22G的对GC完成需要8S的时间,所以这个值在调优的时候写的45%

-XX:+UseStringDeduplication

最近的一项研究表明,应用程序 13.5% 的内存中存在重复的字符串。当你传递 “-XX:+UseStringDeduplication” 参数时,G1 垃圾收集器(G1 GC)提供了一种消除重复字符串的选项。

以上大部分参数要根据实际业务压测。

G1垃圾回收生命周期

Java G1(Garbage-First)垃圾回收器的生命周期包含多个阶段,按触发顺序执行流程可分为以下核心阶段:


1. 年轻代回收(Young GC)

  • 触发条件:Eden 区内存不足时(常规的年轻代回收)。
  • 执行过程
    1. STW 暂停:暂停所有应用线程(Stop-The-World)。
    2. 复制存活对象:将 Eden 区和 Survivor 区的存活对象复制到新的 Survivor 区或直接晋升到老年代。
    3. 调整 Region 归属:回收后的 Eden 区变为空闲 Region。
  • 特点:仅处理年轻代 Region,速度快但频率高。

2. 并发标记周期(Concurrent Marking Cycle)

  • 触发条件:老年代占用达到 IHOP 阈值(默认 45%,通过 -XX:InitiatingHeapOccupancyPercent 调整)。
  • 分阶段执行
    • (1) 初始标记(Initial Mark)
      • STW 暂停:标记从 GC Roots 直接可达的对象(与 Young GC 同步触发)。
    • (2) 根区域扫描(Root Region Scanning)
      • 并发执行:扫描 Survivor 区(根区域)中引用到老年代的对象。
    • (3) 并发标记(Concurrent Marking)
      • 并发执行:遍历堆,标记所有存活对象(与应用线程并行)。
    • (4) 最终标记(Remark)
      • STW 暂停:处理并发标记期间的变化(SATB 算法修正)。
    • (5) 清理(Cleanup)
      • STW 暂停:统计每个 Region 的存活对象占比,为混合回收做准备。
  • 目标:识别老年代中可回收的 Region(标记垃圾占比高的 Region)。

3. 混合回收(Mixed GC)

  • 触发条件:并发标记周期完成后(清理阶段结束)。
  • 执行过程
    1. STW 暂停:暂停应用线程。
    2. 混合回收:同时回收年轻代 Region(Eden/Survivor)和标记出的老年代 Region。
    3. 多轮执行:通过 -XX:G1MixedGCCountTarget 控制单次混合回收的轮次(默认 8 轮)。
  • 特点:兼顾年轻代和老年代回收,减少 Full GC 风险。

4. 完全垃圾回收(Full GC)

  • 触发条件(需尽量避免):
    1. 并发模式失败:并发标记未完成时堆已满。
    2. 晋升失败:存活对象无法晋升到老年代。
    3. 大对象分配失败:Humongous 对象找不到连续 Region。
  • 执行过程
    • 单线程 STW 暂停:串行回收整个堆(类似 Serial GC),性能极差。
  • 优化方向:通过调整 IHOPG1ReservePercent 等参数避免触发。

生命周期流程图

Young GC(常规) → [IHOP 阈值触发] → 并发标记周期 → Mixed GC → Young GC(循环)  ↓  Full GC(异常兜底)

关键注意事项

  1. 并发标记周期是混合回收的前置条件,只有完成标记才能确定老年代可回收 Region。
  2. Mixed GC 是增量回收:通过多轮回收分摊停顿时间。
  3. Full GC 是最后的兜底机制,需通过监控(如 GC 日志)提前规避。

通过调整 -XX:MaxGCPauseMillis(目标停顿时间)和 -XX:G1MixedGCLiveThresholdPercent(回收阈值),可优化混合回收的效率和频率。

参考资料

http://www.aitangshan.cn/news/55.html

相关文章:

  • 面相对象编程:类和对象
  • 学习笔记:Query Transformation- Distinct Aggregate Transformation
  • 安卓
  • 妈妈再也不用担心我画图太丑了,画图神器:plantUML
  • 测试用例精简技术全解析:从原理到实践
  • 优化DeepSpeed ZeRO在低成本硬件上的运行效率
  • 读书笔记:数据库事务处理的那些坑与妙招
  • arduino 工具栏消失
  • # 常见算法板子(一)
  • 【算法分享】字典树 — 插入、查询与状态标记详解
  • 8.10
  • Windows 2003 系统如何修改网卡DNS?
  • Python 内置模块 base64:编码与解码的艺术
  • Webstorm运行显示404 not found的问题解决方案。
  • 一文带你彻底学会 Git 代码管理
  • arcgispro的软件说明文档和使用技巧
  • InnoDB为什么不用跳表,Redis为什么不用B+树?
  • c++算法竞赛输入输出优化
  • JS中对输入的金额进行大写转换(支持两位小数)
  • 集训内容总结 day13:模拟赛 Round6
  • DUBBO通信框架
  • 利用几种阈值法从给定的图像中分割出目标,去除背景
  • centos系统,docker安装失败报错依赖问题。
  • nginx 日志路径配置修改
  • linux 文件命令
  • 8.5.5 编写信号处理程序
  • Dify入门系列(2)| 5 分钟部署 Dify:云服务 vs 本地 Docker
  • 图论杂题选做 20250802
  • EasyExcel 导入/出通用枚举映射
  • dp09