当前位置: 首页 > 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.dtcms.com/a/352404.html

相关文章:

  • 详解 torch.distributed.all_gather_into_tensor
  • 15.examples\01-Micropython-Basics\demo_yield_task.py 加强版
  • 【实时Linux实战系列】基于实时Linux的生物识别系统
  • #Linux内存管理学以致用# 请你根据linux 内核struct page 结构体的双字对齐的设计思想,设计一个类似的结构体
  • 【测试需求分析】-需求来源分析(一)
  • 博士招生 | 香港大学 Intelligent Communication Lab 招收全奖博士
  • 【deepseek问答记录】:chatGPT的参数数量和上下文长度有关系吗?
  • AI Agent正在给传统数据仓库下“死亡通知书“
  • 读《精益数据分析》:用户行为热力图
  • 【拍摄学习记录】01-景别
  • 创龙3576ububuntu系统设置静态IP方法
  • 【Linux 进程】进程程序替换详解
  • 8.26网络编程——Modbus TCP
  • Git 高级技巧:利用 Cherry Pick 实现远程仓库的同步合并
  • 【自然语言处理与大模型】微调数据集如何构建
  • docker 的网络
  • shell默认命令替代、fzf
  • RCC_APB2PeriphClockCmd
  • sdi开发说明
  • 推荐系统王树森(三)粗排精排
  • STM32的Sg90舵机
  • Python入门教程之字符串类型
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(20):文法+单词第7回2
  • iPhone 17 Pro 全新配色确定,首款折叠屏 iPhone 将配备 Touch ID 及四颗镜头
  • 【测试需求分析】-需求类型的初步分析(二)
  • 【NuGet】引用nuget包后构建项目简单解析
  • day41-动静分离
  • 数字时代下的智能信息传播引擎
  • 仿真干货|解析Abaqus AMD的兼容与并行效率问题
  • 基于硅基流动API构建智能聊天应用的完整指南