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

一篇文章拆解Java主流垃圾回收器及其调优方法。

本文将深入且核心的Java性能优化话题。详细拆解Java主流垃圾回收器及其调优方法。

一、 图解概览:垃圾回收器与算法关系

首先,通过下图可以快速了解各垃圾回收器、其使用的算法以及它们之间的组合关系:

Java 垃圾回收器
Serial / Serial Old
单线程收集器
Parallel Scavenge / Parallel Old
吞吐量优先收集器
ParNew / CMS (Concurrent Mark Sweep)
响应时间优先收集器 (老年代)
G1 (Garbage-First)
JDK9+ 全堆收集器
ZGC / Shenandoah
超低延迟收集器 (实验性/生产)
年轻代: 复制算法 (Copying)
老年代: 标记-整理 (Mark-Compact)
年轻代: 复制算法 (Copying)
老年代: 标记-整理 (Mark-Compact)
年轻代 (ParNew): 复制算法 (Copying)
老年代 (CMS):
标记-清除 (Mark-Sweep)
(并发阶段) +
并发失败后备方案: 标记-整理 (Mark-Compact)
整体: 标记-整理 (Mark-Compact)
局部 (Region): 复制算法 (Copying)
ZGC: 着色指针 (Colored Pointers) &
读屏障 (Load Barrier)
Shenandoah: Brooks指针 &
读屏障/写屏障

二、 各垃圾回收器详解与调优

1. Serial / Serial Old
  • 算法
    • 年轻代 (Serial)复制算法 (Copying)。将Eden和Survivor From中存活的对象复制到Survivor To区,然后清空Eden和From。
    • 老年代 (Serial Old)标记-整理算法 (Mark-Compact)。标记存活对象,然后将它们向内存一端移动,清理掉边界以外的内存。
  • 特点单线程,GC时会暂停所有用户线程(Stop-The-World)
  • 适用场景:Client模式下的JVM;内存资源受限的嵌入式系统。
  • 调优:通常无需特意调优。若使用,关注点与其他收集器类似(堆大小、代大小比例)。
2. ParNew
  • 算法复制算法 (Copying)(同Serial)。
  • 特点:Serial收集器的多线程并行版本,是CMS收集器在年轻代的默认搭档。
  • 适用场景:与CMS搭配使用,用于服务端应用的年轻代收集。
  • 调优参数
    • -XX:ParallelGCThreads=N:设置用于年轻代GC的并行线程数。通常设置为等于或略小于CPU核心数。
3. Parallel Scavenge / Parallel Old (JDK 8默认)
  • 算法
    • 年轻代 (Parallel Scavenge)复制算法 (Copying)
    • 老年代 (Parallel Old)标记-整理算法 (Mark-Compact)
  • 特点并行的、多线程的,但其目标是达到一个可控制的吞吐量(Throughput)(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + GC时间))。
  • 适用场景:后台运算、科学计算、批处理任务,不太关心单个请求的延迟。
  • 调优参数
    • -XX:MaxGCPauseMillis=N:设置期望的最大GC停顿时间(毫秒)。JVM会尽力但不保证实现。调小此值可能会增加GC频率,反而降低吞吐量。
    • -XX:GCTimeRatio=N:设置吞吐量目标(0-100)。值是比率,例如19表示GC时间占总时间的1/(1+19)=5%。默认99,即1%的时间用于GC。
    • -XX:+UseAdaptiveSizePolicy(默认开启):开启后,JVM会动态调整年轻代大小(-Xmn)、Eden与Survivor比例(-XX:SurvivorRatio)、晋升老年代年龄阈值(-XX:MaxTenuringThreshold)等参数以接近设定的停顿时间或吞吐量目标。这是Parallel系收集器的一大优势,调优时通常只需设置最大堆(-Xmx)和性能目标(-MaxGCPauseMillis/-GCTimeRatio),细节由JVM自适应完成
4. CMS (Concurrent Mark Sweep)
  • 算法标记-清除算法 (Mark-Sweep)。但为了弥补标记-清除产生的碎片,提供了参数-XX:+UseCMSCompactAtFullCollection(默认开启)在Full GC时进行碎片整理(Mark-Compact)
  • 特点:以获取最短回收停顿时间为目标,大部分GC工作(标记阶段)与用户线程并发执行。
  • 适用场景:Web服务器、B/S系统前端,重视服务的响应速度。
  • 调优参数
    • -XX:CMSInitiatingOccupancyFraction=N至关重要。设置老年代空间使用率达到多少百分比时触发CMS收集(JDK5默认68,JDK6+默认92)。不宜过高或过低。过高可能导致“并发模式失败”(Concurrent Mode Failure),触发Serial Old;过低则GC过于频繁。
    • -XX:+UseCMSInitiatingOccupancyOnly:强制JVM始终使用CMSInitiatingOccupancyFraction的值作为触发条件,而不是动态调整。
    • -XX:ConcGCThreads / -XX:ParallelGCThreads:设置并发标记和并发整理的线程数。通常为ParallelGCThreads的1/4左右。
    • 应对并发模式失败:如果日志中出现Concurrent Mode Failure,说明CMS跟不上对象分配/晋升的速度。解决方案:1) 降低CMSInitiatingOccupancyFraction触发百分比;2) 增加老年代大小(即整个堆大小);3) 增加后台GC线程数;4) 或者换用G1收集器。
5. G1 (Garbage-First) (JDK9+默认)
  • 算法:整体看是标记-整理(Mark-Compact),局部(两个Region之间)是复制算法(Copying)
  • 特点:将堆划分为多个大小相等的Region,优先回收垃圾最多的区域(Garbage-First名的由来),同时能预测停顿时间
  • 适用场景:面向服务端,大内存(>4G)、多核心机器,追求低延迟且吞吐量也不错的通用型收集器。
  • 调优参数
    • -XX:MaxGCPauseMillis=N:设置期望的最大停顿时间目标(如200ms)。这是G1的核心调优参数,G1会尽力通过调整每次回收的Region数量来达成目标。
    • -XX:InitiatingHeapOccupancyPercent=N(IHOP):设置整个堆使用率达到多少时触发并发标记周期(默认45%)。这是G1的“老年代”填充触发机制。如果Mixed GC来不及回收,导致Full GC,可以调低此值,让G1更早开始标记。
    • -XX:ConcGCThreads=N:设置并发标记阶段的线程数。
    • Region大小:使用-XX:G1HeapRegionSize=M设置,范围1M-32M,必须是2的幂。G1会自动计算,通常无需手动指定。
6. ZGC & Shenandoah
  • 算法:并非传统分代算法,而是基于Region的、几乎全阶段并发的革命性算法。ZGC使用着色指针(Colored Pointers)和读屏障(Load Barrier) 来实现并发整理。
  • 特点超低停顿(通常<10ms,且几乎不随堆大小增长而增加),高吞吐量损耗。
  • 适用场景超大堆(TB级别)且对延迟极度敏感的应用(如金融交易、实时系统)。
  • 调优:目前调优目标相对简单,主要是设定最大停顿时间。
    • -XX:MaxGCPauseMillis=N:同样用于设定停顿时间目标。
    • 分配速率:这类收集器的性能瓶颈往往在于对象的分配速率(Allocation Rate)。如果分配太快,GC跟不上,也会触发STW。优化代码,减少对象分配是根本。

三、 通用调优思路与步骤

  1. 明确目标:是追求高吞吐量(Parallel Scavenge/Old)还是低延迟(CMS, G1, ZGC)?这是选择收集器的首要依据。
  2. 监控先行没有监控就没有调优。使用jstat, gcviewer, GCeasy等工具分析GC日志,关注:
    • YoungGC/FulllGC频率和耗时
    • 吞吐量停顿时间
    • 老年代内存变化趋势
    • 是否出现Concurrent Mode FailurePromotion Failed等错误。
  3. 设置堆大小-Xms-Xmx设为相同值,避免堆震荡。
  4. 选择收集器:根据目标和监控结果选择。
    • JDK8:吞吐量选 -XX:+UseParallelGC,低延迟选 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
    • JDK11+:优先选择G1 (-XX:+UseG1GC),超大堆或极致延迟选ZGC (-XX:+UseZGC)。
  5. 精细化调优:根据收集器特性设置上述提到的关键参数(如CMS的CMSInitiatingOccupancyFraction,G1的MaxGCPauseMillis)。
  6. 检查代码:很多时候GC问题的根源是代码问题,如内存泄漏、过度分配、不必要的对象创建等。JVM调优是最后的手段,而非首选

希望这份详细的总结能帮助读者更好地理解和调优Java垃圾回收器!

http://www.xdnf.cn/news/1369513.html

相关文章:

  • LeetCode-22day:多维动态规划
  • 代码随想录Day62:图论(Floyd 算法精讲、A * 算法精讲、最短路算法总结、图论总结)
  • vue2和vue3的对比
  • TensorFlow 深度学习:使用 feature_column 训练心脏病分类模型
  • Day3--HOT100--42. 接雨水,3. 无重复字符的最长子串,438. 找到字符串中所有字母异位词
  • CentOS 7 服务器初始化:从 0 到 1 的安全高效配置指南
  • 肌肉力量训练
  • 木马免杀工具使用
  • 产品经理操作手册(3)——产品需求文档
  • 全链路营销增长引擎闭门会北京站开启倒计时,解码营销破局之道
  • 构建生产级 RAG 系统:从数据处理到智能体(Agent)的全流程深度解析
  • 书生大模型InternLM2:从2.6T数据到200K上下文的开源模型王者
  • word批量修改交叉引用颜色
  • 【SystemUI】新增实体键盘快捷键说明
  • 常用Nginx正则匹配规则
  • ruoyi-vue(十二)——定时任务,缓存监控,服务监控以及系统接口
  • 软件检测报告:XML外部实体(XXE)注入漏洞原因和影响
  • 服务器初始化流程***
  • 在分布式环境下正确使用MyBatis二级缓存
  • 在 UniApp 中,实现下拉刷新
  • Python爬虫: 分布式爬虫架构讲解及实现
  • IjkPlayer 播放 MP4 视频时快进导致进度回退的问题
  • iOS 26 正式版即将发布,Flutter 完成全新 devicectl + lldb 的 Debug JIT 运行支持
  • 深度学习(三):PyTorch 损失函数:按任务分类的实用指南
  • Milvus介绍及多模态检索实践
  • 系统设计中的幂等性
  • 【LeetCode 热题 100】31. 下一个排列
  • 进入docker中mysql容器的方法
  • Linux(二十二)——服务器初始化指南
  • 把 shell 脚本里的「后台接收」-- 以 UART/CAN 双总线监听为例