Java线上频繁FullGC的完整排查流程
更新时间:2026年06月05日 09:33:01 作者:码不停蹄的玄黓
这段文章详细介绍了FullGC的的常见触发原因原因、排查思路和优化方法,涵盖老年代不足、内存泄漏、MetaSpace元空间满等方面,并提供了从紧急应急到长期优化的全面解决方案,需要的朋友可以参考下
一、先明确:FullGC触发常见原因
- 老年代空间不足、内存泄漏/内存溢出
- 大对象直接进老年代、频繁晋升
- MetaSpace元空间满、动态加载类过多
- System.gc()代码主动调用、RMI定时FullGC
- JVM参数不合理:新生代太小、Survivor过小、晋升阈值异常
- 堆内存分配过小、物理内存被其他进程抢占
核心排查思路:先抓现场 → 看GC日志定位类型 → 堆快照分析 → 代码定位 → 压测复现 → 调参优化
二、阶段1:紧急应急(线上不能停服优先)
1. 基础信息采集
# 查看java进程PID jps -l # 实时看GC状态 jstat -gcutil PID 1000
重点指标:
- O(老年代使用率)持续上涨 → 内存泄漏/对象不停晋升到老年代
- M(元空间)持续涨满 → 类加载泄漏
- S0/S1长期几乎满、Eden快速打满 → 新生代过小,对象频繁提前晋升老年代
2. 临时规避(业务优先保可用)
- 临时扩容-Xmx/-Xms,增大堆,缓解频繁FGC
- 临时关闭RMI自动FullGC:
-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 - 临时下线可疑定时任务、批量导入、大文件解析接口
三、阶段2:采集GC日志,定位FGC根因分类
1. 开启/导出GC日志(没提前配置则动态抓取)
JVM启动参数必备(生产标配):
-Xloggc:/xxx/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
根据GC日志区分4大类FGC:
- 老年代占满触发FGC(最常见)
日志关键字:Allocation Failure,Eden耗尽→对象晋升老年代,老年代不足触发FullGC - MetaSpace满触发FGC
日志:Metaspace allocation failure,动态代理、热加载、频繁创建ClassLoader - System.gc()主动触发
日志:Full GC (System.gc()),代码/第三方包显式调用System.gc() - CMS并发失败/晋升担保失败(CMS收集器)
concurrent mode failure / promotion failed:老年代预留空间不足,担保失败触发STW FullGC
四、阶段3:dump堆快照,定位占用内存的对象(核心步骤)
抓堆时机:FGC频繁、老年代占用率80%+还没OOM时dump,避免OOM后数据丢失
方式1:命令行dump
jmap -dump:format=b,file=heap.hprof PID # 生产dump建议加参数,不阻塞业务: jmap -dump:live,format=b,file=heap.hprof PID
方式2:OOM自动dump(提前配置)
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heap.hprof
堆文件分析工具
- MAT(Eclipse Memory Analyzer,首选)
- Leak Suspects:泄漏可疑报告(一键定位泄漏对象)
- Dominator Tree:支配树,查看占用堆最多的对象
- Histogram:按类统计实例数量、占用内存
- JProfiler、Arthas在线分析(不用下载大hprof)
五、阶段4:Arthas在线实时排查(不用重启、不用dump大文件)
1. 安装启动Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar
常用排查命令
# 1. 实时监控GC gc -i 1000 # 2. 查看堆内存实时分布 heapdump --live /tmp/dump.hprof # 3. 查看对象实例数量,快速找暴涨对象 dashboard # 4. 追踪创建大量对象的方法 trace 全类名 方法名 # 5. 排查ClassLoader泄漏 classloader
六、阶段5:按场景逐个定位代码问题
场景1:内存泄漏(老年代缓慢涨,每次FGC回收不掉多少内存)
常见泄漏点:
- 静态集合(static List/Map)无限add对象,无移除逻辑
- ThreadLocal使用完未remove,线程池复用线程导致对象常驻
- 连接池、缓存(本地Cache)无过期淘汰,数据只增不减
- 第三方框架:Mybatis拦截器、定时任务、异步线程未销毁对象
MAT定位:支配树找到超大对象 → 查看引用链Reference Chain → 找到业务代码
场景2:大对象频繁创建直接进老年代
- 一次性读取超大文件、全表查数据库不分页返回全量List
- 超大字符串拼接、一次性创建超大数组
JVM参数:-XX:PretenureSizeThreshold=2M超过阈值直接进老年代,频繁创建触发FGC
场景3:元空间满FGC
- 动态生成代理类(CGLIB频繁创建)、Groovy脚本热加载
- 自定义ClassLoader不停新建不卸载,
-XX:MaxMetaspaceSize配置过小
场景4:代码主动System.gc()
- 第三方SDK、老版本RPC、定时任务里隐式调用System.gc()
- 代码检索
System.gc()全局关键字,使用Arthaswatch拦截调用
七、阶段6:JVM参数优化(分收集器:G1/CMS/ZGC)
通用优化方向
- 新生代大小:Eden占堆1/3~1/2,避免Eden过小频繁晋升
- 调整SurvivorRatio、MaxTenuringThreshold晋升年龄,减少过早晋升到老年代
- 禁用显式GC:
-XX:+DisableExplicitGC(谨慎:NIO依赖System.gc堆外内存则不能加) - 合理设置MetaSpace:
-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M
G1收集器(主流生产)
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 # 预期STW停顿 -XX:G1HeapRegionSize=16M
CMS老项目优化
- 调高老年代预留空间:
-XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly,避免并发失败FullGC
八、阶段7:复现验证+上线落地优化
- 本地/测试环境压测(JMeter)复现FGC,修复代码后压测验证GC平稳
- 上线灰度发布,上线后持续监控GC指标(Prometheus+Grafana)
- 告警配置:老年代使用率>80%、10min内FullGC>3次触发告警
九、排查速记口诀
先jstat看GC趋势 → 拉GC日志分FGC类型 → arthas在线查对象 → dump堆MAT找泄漏引用 → 改代码+调JVM参数 → 压测验证上线
到此这篇关于Java线上频繁FullGC的完整排查流程的文章就介绍到这了,更多相关Java线上频繁FullGC内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Spring Security如何基于Authentication获取用户信息
这篇文章主要介绍了Spring Security如何基于Authentication获取用户信息,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2020-03-03


最新评论