需先用jps -l和jps -v查Java进程ID及JVM参数;再用jstat监控GC频率与堆分布;jstack分析线程阻塞与死锁;jmap -histo定位内存泄漏对象;大堆推荐用JFR替代jmap。

Java进程卡顿、GC频繁、内存溢出,光看日志很难定位——得用对命令和工具,否则调优就是拍脑袋。
怎么快速查Java进程ID和基础JVM参数
没进程ID,后续所有命令都无从下手;不确认启动参数,比如-Xmx或-XX:+UseG1GC,就容易误判问题根源。
- 用
jps -l列出所有Java进程的完整类路径,配合grep快速过滤,比如jps -l | grep MyApp -
jps -v能直接看到JVM启动时传入的参数(含-Xms、-Xmx、GC类型等),比翻部署脚本快得多 - 如果进程已挂但残留了
hs_err_pid*.log,说明发生过致命错误,优先检查该文件里的Current thread和Heap段
jstat看GC频率和堆分布是否合理
jstat是唯一能在不重启、不侵入应用的前提下实时观察GC行为的命令,但参数选错就等于白看。
- 查G1 GC:用
jstat -gc -h10,重点关注1s G1-YGC(年轻代次数)和G1-FGC(Full GC次数)——如果G1-FGC非零,基本可断定元空间泄漏或大对象晋升失败 - 查堆各区域占比:用
jstat -gccapacity,若NGCMN/NGCMX接近,说明年轻代可能被硬编码限制,动态扩容失效 - 注意单位:
jstat输出默认是KB,不是MB;S0C和S1C长期为0,可能是Survivor空间被G1自动忽略(正常)
jstack抓线程死锁和长时间BLOCKED
接口响应慢但CPU不高?大概率是线程在等锁。jstack不是用来数线程数的,是用来找“谁在等谁”。
立即学习“Java免费学习笔记(深入)”;
- 执行
jstack后,搜> threaddump.txt java.lang.Thread.State: BLOCKED,再顺着waiting to lock找到持有该锁的线程 - 有死锁?
jstack -l会明确标出Found one Java-level deadlock:,并列出互相等待的线程栈 - 避免信号干扰:生产环境慎用
jstack -F(强制dump),可能触发JVM内部锁竞争,反而加剧卡顿
jmap生成堆转储后别直接用MAT打开大文件
动辄几个GB的heap.hprof,MAT加载慢、易OOM,真正要查的是“谁占了最多对象”和“谁阻止了回收”,不是全量分析。
- 先用
jmap -histo:live快速看存活对象统计,重点关注[B(byte[])、java.util.HashMap$Node、自定义缓存类——它们常是内存泄漏源头 - 生成转储用
jmap -dump:format=b,file=heap.hprof,加:live参数只dump存活对象,减少文件体积 - 大堆建议用
jdk.jfr替代:启动时加-XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,filename=recording.jfr,事后用JDK自带jdk.jfr命令或JMC分析,开销更低
真正难的不是命令怎么敲,而是把jstat里跳变的YGC时间、jstack里重复出现的锁地址、jmap -histo里持续增长的对象数,串成一条因果链——这需要对应用代码路径和JVM内存模型都有具体认知,而不是背参数。











