Java面试——JVM性能调优常用命令与工具

需先用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 threadHeap

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;S0CS1C长期为0,可能是Survivor空间被G1自动忽略(正常)

jstack抓线程死锁和长时间BLOCKED

接口响应慢但CPU不高?大概率是线程在等锁。jstack不是用来数线程数的,是用来找“谁在等谁”。

  • 执行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分析,开销更低

真正难的不是命令怎么敲,而是把j

stat里跳变的YGC时间、jstack里重复出现的锁地址、jmap -histo里持续增长的对象数,串成一条因果链——这需要对应用代码路径和JVM内存模型都有具体认知,而不是背参数。