一、JVM核心排查命令详解(生产必备)
JDK自带一套轻量化命令行工具,无需额外部署,线上故障第一时间优先使用,是JVM调优的基础核心。jmap是一个高危命令,执行时会暂停目标 JVM 进程(Stop-The-World),在生产环境高负载场景下慎用,可能导致服务短暂不可用。
1.1 jmap 内存映像命令
作用:查看JVM堆内存布局、对象实例统计、内存占用、导出堆dump文件,专门用于内存溢出、内存泄漏、大对象堆积排查。
命令格式:jmap [参数] 进程PID
1.1.1 全参数详解
jmap -heap PID:查看堆内存整体配置与实时占用,包含新生代、老年代、元空间大小、使用比例、GC算法、内存阈值,适合快速判断堆内存配置是否合理。结果示例图如下:
jmap -histo PID:打印堆中所有类的实例数量、占用内存大小,按内存排序,快速定位大对象、异常膨胀对象。结果示例图如下:
num:序号
instances:实例数量
bytes: 占用空间大小
class name:类名称 ,[C is a char[] ,[S is a short[] ,[I is a int[] ,[B is a byte[] ,[[I is a int[][]
jmap -histo:live PID:只统计存活对象,过滤已死亡垃圾对象,数据更精准,生产优先使用。
jmap -dump:format=b,file=xxx.hprof PID:导出完整堆内存dump文件,用于离线深度分析。
jmap -dump:live,format=b,file=xxx.hprof PID:只导出存活对象dump,文件体积更小、分析效率更高。
jmap -finalizerinfo PID:查看等待Finalizer回收的对象数量,排查对象终结器阻塞问题。
jmap -clstats PID:查看类加载器、类加载统计数据,排查元空间溢出、类泄露问题。
1.2 堆内存Dump文件详解与使用方式
1.2.1 Dump文件介绍
Heap Dump 是JVM某一时刻的堆内存快照文件,保存所有对象实例、引用关系、内存占用、线程堆栈信息,是排查OOM、内存泄漏、对象堆积的最核心依据。
分为两种:
全量dump:包含存活+死亡对象,文件大、信息全;
live dump:仅存活对象,体积小、定位问题更精准,生产推荐。
1.2.2 Dump文件常用使用场景与方式
主动导出:线上内存飙升、接口卡顿,手动执行jmap导出快照,用于事后分析。
自动导出(推荐):JVM启动参数配置
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heap.hprof,OOM时自动dump,不漏现场。
1.2.3 Dump文件分析工具
MAT(Memory Analyzer Tool):行业标准,分析大对象、泄漏链路、支配树、重复对象;
JVisualVM:JDK自带,轻量可视化查看内存快照;
IDEA Profiler:本地开发快速分析dump文件。
1.3 jstack 线程堆栈命令
作用:生成JVM瞬时线程快照,排查线程死锁、线程阻塞、死循环、CPU飙高、线程堆积问题。
1.3.1 常用命令与参数介绍
jstack PID:打印所有线程堆栈信息、线程状态、调用链路;
jstack -l PID:长列表模式,打印锁详细信息,专门排查死锁;
jstack -F PID:强制导出线程堆栈,应对进程卡死、无响应场景;
jstack PID > thread.log:输出日志落地,用于离线分析。
1.3.2 如何查询CPU最高的线程堆栈信息(生产高频)
线上Java服务CPU 100%、负载过高,精准定位问题线程步骤:
top 查看Java进程PID,确认CPU占用最高进程;
top -H -p PID 查看该进程下所有线程CPU占用,找到最高线程TID;
printf "%x\n" TID 将线程ID转为16进制(jstack日志线程ID为16进制);
jstack PID | grep -A 20 十六进制TID 精准打印问题线程堆栈,定位死循环、复杂计算、阻塞代码。示例图如下:
"Thread-1" 线程名
prio=5 优先级=5
tid=0x000000001fa9e000 线程id
nid=0x2d64 线程对应的本地线程标识nid
java.lang.Thread.State:BLOCKED线程状态
适用场景:代码死循环、正则回溯死锁、大批量同步计算、线程池任务堆积。
1.4 jinfo 运行参数查看命令
1.4.1 命令介绍
作用:实时查看、动态修改JVM运行参数,无需重启服务,快速核对线上JVM配置是否与预期一致。
常用命令:
jinfo PID:查看全部JVM参数、系统属性;
jinfo -flags PID:查看所有JVM启停参数、GC参数、优化参数;
jinfo -sysprops PID:查看Java系统运行参数;
jinfo -flag 参数名 PID:单独查看某一个参数配置。
1.4.2 结果介绍
jinfo输出结果分为两类:
非默认参数:用户启动脚本手动配置的Xmx、Xms、GC收集器、栈大小等;
默认参数:JVM自动适配的默认参数,可快速发现参数缺失、配置错误、GC收集器不匹配问题。
核心价值:解决“本地正常、线上异常”的配置不一致问题。
1.5 jstat 全能统计命令(JVM调优核心)
作用:实时监控JVM堆内存、新生代、老年代、元空间、GC次数、GC耗时、类加载情况,是生产监控JVM运行状态的第一命令。
命令通用格式:jstat -[选项] PID 刷新间隔(ms) 打印次数
1.5.1 常用命令总览
gc:整体垃圾回收统计
gccapacity:堆内存整体容量统计
gcnew:新生代GC统计
gcnewcapacity:新生代内存容量统计
gcold:老年代GC统计
gcoldcapacity:老年代容量统计
gcmetacapacity:元空间统计
class:类加载卸载统计
1.5.2 通用结果参数介绍
S0/S1:Survivor0、Survivor1使用占比
E:Eden区使用占比
O:老年代使用占比
M:元空间使用占比
YGC:年轻代GC总次数
YGCT:年轻代GC总耗时
FGC:FullGC总次数
FGCT:FullGC总耗时
GCT:GC总耗时
1.5.3 垃圾回收整体统计查看
命令:jstat -gc PID 1000 10
释义:每1000ms打印一次GC整体统计,共打印10次,实时监控GC频率与耗时。
示例图如下:
S0C:第一个幸存区的大小,单位KB
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小(元空间)
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s
1.5.4 堆内存整体统计查看
命令:jstat -gccapacity PID 1000
释义:查看新生代、老年代、元空间最大最小容量、当前占用,判断内存分配是否过剩/不足。
示例图如下:
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数
1.5.5 新生代垃圾回收统计
命令:jstat -gcnew PID 1000
释义:精准监控新生代MinorGC频率、单次耗时、存活对象情况,排查新生代过小、GC过于频繁问题。
示例图如下:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
1.5.6 新生代内存统计
命令:jstat -gcnewcapacity PID 1000
释义:查看Eden、S0、S1分区容量变化,判断动态扩容是否频繁、分区比例是否合理。
示例图如下:
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数
1.5.7 老年代垃圾回收统计
命令:jstat -gcold PID 1000
释义:监控老年代GC、MajorGC、FullGC触发情况,排查老年代对象溢出、大对象堆积。
示例图如下:
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
1.5.8 老年代内存统计
命令:jstat -gcoldcapacity PID 1000
释义:查看老年代最大、最小、当前容量,判断老年代是否过小导致频繁FullGC。
示例图如下:
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
1.5.9 元数据空间统计
命令:jstat -gcmetacapacity PID 1000
释义:监控元空间占用、扩容情况,排查类加载过多、动态代理泛滥导致的元空间OOM。
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
1.5.9 GC 统计
命令:jstat -gcutil PID
释义:用来实时查看 Java 堆各内存区域使用率、GC 次数 / 耗时,日常排查内存泄漏、GC 频繁、卡顿最常用。
示例图如下:
S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
1.6 JVM运行状态估算逻辑(调优核心思维)
真正的JVM调优不是堆砌参数,而是基于运行数据估算内存模型、预判GC趋势、针对性优化。
1.6.1 年轻代对象增长速率
通过jstat每秒Eden区增长量,判断业务瞬时对象生成速率:
增长过快:新生代过小 → 频繁MinorGC → 接口抖动;
增长平稳:新生代大小合理,无需扩容。
1.6.2 YoungGC触发频率和耗时
健康标准:秒级服务YGC频率可控,单次YGC耗时<20ms;
异常判定:每秒多次YGC、单次耗时过长,说明新生代内存不足、短期对象爆炸。
1.6.3 YGC后存活对象与晋升比例
每次YGC后少量对象存活、正常晋升老年代为健康状态;通过对比前后的对象大小可以推算对应老年代对象增长速率
异常场景:YGC后大量对象存活并晋升 → 老年代快速填满 → 频繁FullGC。
1.6.4 FullGC触发频率和耗时
生产红线:1小时内FullGC次数不应超过2次,单次FullGC耗时不可超过100ms;
频繁FullGC、耗时暴涨,直接导致系统卡顿、超时、雪崩。
1.6.5 通用优化思路
根据对象增长速率扩容新生代,降低YGC频率;
控制对象晋升比例,控制YGC的存活对象大小,尽量少于Survivor区域的50%,避免大量短期对象进入老年代;
优化大对象分配,避免直接进入老年代造成空间占用;
合理设置GC阈值,提前并发回收,避免内存打满触发STW FullGC;
修复内存泄漏,杜绝老年代只增不减。
二、常用JVM调优可视化工具介绍
2.1 JVisualVM(JDK自带)
免费轻量、零部署,支持实时监控堆内存、线程、GC、CPU、dump文件分析、死锁检测,适合快速排查线上、本地问题。
2.2 MAT(Memory Analyzer Tool)
行业标准dump分析工具,擅长内存泄漏定位、大对象分析、对象引用链、支配树分析,生产OOM问题首选。
2.3 Arthas(阿里开源)
Java线上诊断神器,实时监控方法耗时、线程状态、内存变化、动态追踪代码,无需重启服务,精准定位业务代码导致的GC异常、CPU飙升。
2.4 GCEasy
GC日志在线分析工具,上传gc.log即可生成可视化报表,自动分析GC频率、停顿时间、异常GC、吞吐量损耗,新手友好。
三、内存泄漏原理以及优化方案
3.1 内存泄漏核心原理
内存泄漏:程序中已失效对象仍然被强引用持有,GC无法回收,导致堆内存只增不减,最终内存耗尽触发OOM。
核心本质:存活无效对象堆积,占用有效内存。
3.2 常见内存泄漏场景
静态集合(static List/Map)无限堆积对象;
未关闭IO流、数据库连接、网络连接;
线程池线程长期持有任务引用、线程不销毁;
内部类、匿名类持有外部类强引用;
缓存无过期策略、无淘汰机制;
WeakHashMap使用不当、失效数据不清理。
3.3 内存泄漏排查步骤
jstat观察老年代内存持续上涨、GC无法释放;
jmap导出live堆快照;
MAT分析大对象、溢出对象、超长引用链;
定位代码泄漏位置,修复引用持有问题。
3.4 内存泄漏通用优化方案
集合使用完毕主动clear,避免静态集合滥用;
IO、连接资源使用try-with-resources自动关闭;
缓存增加LRU淘汰、过期时间、定时清理;
使用弱引用WeakReference、软引用SoftReference存储临时对象;
线程池合理配置核心线程、最大线程、空闲超时,避免线程常驻堆积。
四、系统频繁FullGC导致系统卡顿真实优化方案
4.1 问题现象
高并发电商业务,高峰期接口超时、系统卡顿、CPU抖动、监控显示频繁FullGC、FGCT持续上涨,吞吐量大幅下降。
4.2 常见问题分析流程
jstat观察:YGC频繁、老年代占用快速上涨、FullGC几分钟一次;
根据JVM的参数配置反推当前系统的JVM内存配置模型,查看各个区的大小配置是否存在问题并修改对应的参数配置
通过jstat查看对应的YGC和FullGC的清理情况是否异常
推测异常的可能原因,例如1.代码执行
System.gc(),2.年轻代对象频繁晋升到老年代,3.大对象直接进入老年代4.Minor GC 空间担保失败5.元空间 / 永久代溢出针对大对象可以通过jmap分析,查看是哪些大对象在占用内存
如果大对象的调用少,直接搜索代码完成筛选分析,如果调用点过多,通过jstack来定位cpu使用较高的线程以及对应的代码位置来定位最终的问题点。
4.3 优化核心逻辑
扩容新生代,优化代码减少临时对象,容纳瞬时海量临时对象,减少YGC频率;
提高晋升年龄,拦截短期对象,避免老年代快速膨胀;
提前触发CMS并发GC,避免内存打满降级STW FullGC;
定时内存碎片整理,解决大对象分配失败问题。
调整
SurvivorRatio、增大幸存区,减少老年代的增长速率合理设置堆大小、分代比例
拆分大对象、调大阈值、检查内存泄漏
4.4 优化效果
FullGC从频繁触发降至每日数次;
GC停顿稳定在20ms以内;
高峰期接口超时、系统卡顿完全消失;
服务长期平稳运行,无内存堆积、无OOM。
五、全文总结
1、JVM调优的基础是命令行排查能力:jmap定位内存、jstack定位线程、jinfo定位参数、jstat监控GC运行趋势。
2、通过jstat数据可精准估算对象增长速率、GC频率、晋升比例、FullGC健康度,是参数优化的核心依据。
3、内存泄漏的本质是无效对象强引用堆积,必须结合dump快照+MAT工具定位代码层级问题。
4、频繁FullGC卡顿优化核心思路:控晋升、扩新生代、提前并发回收、整理内存碎片、修复泄漏,从根源解决GC抖动。
掌握全套排查命令、运行估算逻辑、故障优化方案,可独立搞定线上99%的JVM性能问题。