【JVM】相关排查命令
2026/7/18 3:00:25 网站建设 项目流程

【JVM】相关排查命令

  • 【一】JVM 故障排查全套 Linux 命令、参数、实战案例
    • 【1】前置说明
      • (1)jps:定位 Java 进程 PID(入口命令)
      • (2)top /htop:定位 CPU 占用高的 Java 线程
        • 1-top(系统自带,无需额外安装)
        • 2-htop(可视化,需 yum install htop)
      • (3)jstack:打印线程堆栈,排查 CPU 高、死锁、阻塞
      • (4)jstat:轻量实时监控 GC、分代内存(线上最常用,无性能损耗)
        • 1-jstat -gc PID 间隔毫秒 打印次数
        • 2-jstat -gccapacity PID 1000
        • 3-jstat -gcmetacapacity PID 1000
        • 4-jstat -gcutil PID 1000
      • (5)jmap:堆内存快照、查看内存占用(排查 OOM)
        • 1-jmap -heap PID:轻量查看堆分代配置,不生成 dump
        • 2-jmap -histo PID:轻量堆对象统计(不拉完整 dump)
        • 3-jmap -dump:format=b,file=xxx.hprof PID 生成堆快照
        • 4-jmap -clstats PID:类加载统计,排查元空间
      • (6)free /df/vmstat:系统底层资源排查
  • 【二】GC 日志完整分析方法:区分新生代 / 老年代 / 元空间问题
    • 【1】JVM GC 日志开启参数(生产必配)
    • 【2】GC 日志关键字段解读
      • (1)Young GC 日志样例
      • (2)Full GC 日志样例(危险)
    • 【3】如何判断哪个区域出问题
      • (1)新生代 Eden 问题
      • (2)老年代 Old Gen 问题(堆内存泄漏)
      • (3)元空间 Metaspace 问题(元空间泄漏)
      • (4)补充区分特征
  • 【三】堆内存监控:轻量分析方案(不生成 dump 大文件)
    • 方案 1:jstat 持续监控(生产首选,零 IO、低性能损耗)
    • 方案 2:jmap -heap 查看分代配置与实时占用
    • 方案 3:jmap -histo 轻量对象统计(定位泄漏对象)
    • 方案 4:可视化轻量工具 Arthas(阿里开源,推荐线上)
  • 【四】Heap Dump(hprof)快照是否包含元空间数据?
      • 细分说明
      • 如果要完整分析元空间问题配套方案
      • 补充:OOM 元空间时仅靠 hprof 不够
  • 【五】完整排查流程总结

【一】JVM 故障排查全套 Linux 命令、参数、实战案例

【1】前置说明

所有命令依赖 JDK 自带工具包(jps/jstack/jstat/jmap/jhat),需和运行 Java 进程同版本 JDK,且进程同用户。

(1)jps:定位 Java 进程 PID(入口命令)

作用:列出本机所有 Java 进程、PID、主类名、JVM 参数
常用参数

  • jps -l:输出完整主类全限定名(最常用)
  • jps -m:输出 main 方法入参
  • jps -v:输出进程启动全部 JVM 参数(Xmx/Xss/ 元空间参数等)
  • jps -q:只输出 PID,脚本批量处理用

实战案例

# 查看所有Java进程完整类名jps-l# 输出示例12568com.server.Application9872org.apache.tomcat.startup.Bootstrap# 查看进程12568的所有JVM启动参数jps-v12568

(2)top /htop:定位 CPU 占用高的 Java 线程

1-top(系统自带,无需额外安装)

作用:查看整机 CPU、内存,找到高 CPU PID;再定位内部高消耗线程
核心操作与参数

top# 进入面板Shift + P# 按CPU使用率排序Shift + M# 按内存占用排序1# 展开多核CPU

完整排查 CPU 飙高流程案例

# 1. top 找到Java进程PID=12568top# 2. 单独只监控该进程top-p12568# 3. 导出该进程下所有线程(nid十六进制,用于jstack匹配)top-H-p12568# 4. 假设高CPU线程nid=3241,转16进制(printf "%x\n" 3241)printf"%x\n"3241# 输出 c91# 5. jstack打印堆栈,搜索0xc91定位死循环/频繁GC线程jstack12568>thread.loggrep-A20c91 thread.log
2-htop(可视化,需 yum install htop)
htop-p12568# F6 按CPU排序,直接看线程占用

(3)jstack:打印线程堆栈,排查 CPU 高、死锁、阻塞

作用:导出所有线程栈,定位死循环、死锁、阻塞、GC 线程耗时
常用参数

  • jstack PID:普通堆栈
  • jstack -l PID:额外输出锁持有信息(排查死锁必用)
  • jstack -F PID:进程假死、阻塞无响应时强制 dump 线程

实战案例

# 导出线程栈到文件jstack-l12568>jstack_$(date+%Y%m%d_%H%M).log# 连续3次打印堆栈,对比线程状态(排查持续CPU高)jstack12568>>stack1.logsleep2jstack12568>>stack2.logsleep2jstack12568>>stack3.log# 对比同一线程是否一直RUNNABLE死循环

常见问题关键字:

  • RUNNABLE循环业务代码 → 业务死循环 CPU 高
  • WAITING / BLOCKED→ 锁竞争、死锁
  • GC Thread大量占用 → GC 频繁导致 CPU 飙升

(4)jstat:轻量实时监控 GC、分代内存(线上最常用,无性能损耗)

核心三大用法:jstat -gc/-gccapacity/-gcmetacapacity

1-jstat -gc PID 间隔毫秒 打印次数

作用:实时输出新生代、老年代、元空间内存、GC 次数、耗时(轻量监控首选,无需 dump)
字段说明

S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT S0C/S1C:Survivor总容量;S0U/S1U:已使用 EC/EU:Eden总容量/已用 OC/OU:老年代总容量/已用 MC/MU:元空间总容量/已使用 YGC/YGCT:YoungGC总次数、总耗时 FGC/FGCT:FullGC总次数、总耗时 GCT:GC总耗时

实战案例

# 每500ms打印一次GC状态,持续输出jstat-gc12568500# 每1秒打印,输出20次后停止jstat-gc12568100020

判断问题技巧:

  1. OU(老年代已用)持续上涨,FGC 持续增多 → 堆内存泄漏
  2. MU(元空间已用)只涨不跌,FGC 频繁 → 元空间泄漏
  3. YGC 次数极多,YGCT 耗时高 → Eden 过小,分配对象过快
  4. FGCT 持续走高,每次 FullGC 停顿久 → 老年代不足 / 元空间达阈值
2-jstat -gccapacity PID 1000

查看各代内存容量、最大阈值(Xmx、MaxMetaspaceSize 生效值)

3-jstat -gcmetacapacity PID 1000

专门监控元空间:MC/MU、MCMX(最大元空间)、类加载卸载数量,排查元空间泄漏专用

4-jstat -gcutil PID 1000

百分比展示内存使用率,直观

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT0.0088.3212.4576.2392.1289.11321015.2104228.32043.530

O=76%、M=92% 代表老年代、元空间占用极高,马上 OOM

(5)jmap:堆内存快照、查看内存占用(排查 OOM)

1-jmap -heap PID:轻量查看堆分代配置,不生成 dump

作用:打印堆整体配置、新生代 / 老年代 / 元空间容量、收集器类型,轻量无 IO 开销
案例:

jmap-heap12568# 输出:Xms/Xmx、Eden/Survivor比例、MaxTenuringThreshold、Metaspace配置、GC收集器
2-jmap -histo PID:轻量堆对象统计(不拉完整 dump)

作用:实时打印堆内所有对象类型、实例数量、占用内存大小,快速定位大对象 / 泄漏对象

# 输出到文件分析jmap-histo12568>histo.log# 只看前30行占用最大的对象head-30histo.log

字段:序号 | 实例数量 | 占用字节 | 类名
发现自定义业务类持续大量创建 = 堆泄漏

3-jmap -dump:format=b,file=xxx.hprof PID 生成堆快照

作用:导出完整堆 dump 文件,用于 MAT 深度分析

# 导出堆快照到指定路径jmap-dump:format=b,file=/data/heap_$(date+%Y%m%d).hprof12568# OOM自动dump参数(启动JVM时配置,无需手动执行命令)-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/data/dump
4-jmap -clstats PID:类加载统计,排查元空间

输出加载类总数、卸载数量、元空间占用

(6)free /df/vmstat:系统底层资源排查

free-h# 整机物理内存、Swap,Swap持续上涨说明内存不足vmstat1# 每秒系统IO、内存、CPU,si/so代表swap交换频繁(GC卡顿)df-h# 磁盘满会导致dump文件无法生成、GC日志写失败

【二】GC 日志完整分析方法:区分新生代 / 老年代 / 元空间问题

【1】JVM GC 日志开启参数(生产必配)

# 基础GC日志-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCDateStamps-Xloggc:/data/logs/gc-%t.log# 打印元空间、类卸载日志-XX:+PrintMetaspaceStatistics-XX:+TraceClassUnloading# 打印GC前后各代内存-XX:+PrintHeapAtGC

【2】GC 日志关键字段解读

(1)Young GC 日志样例

2026-07-17T10:00:00.123+0800:12345.123:[GC(Allocation Failure)[PSYoungGen: 81920K->800K(92160K)]120000K->40880K(194560K),0.012345secs][Metaspace: 32100K->32100K(108032K)]

拆解:

  1. Allocation Failure:Eden 区满,分配新对象失败,触发 YoungGC
  2. PSYoungGen: 81920K->800K:新生代 GC 前 80M,回收后 0.8M,回收效果好
  3. 整体堆:120000K→40880K
  4. Metaspace:32100K->32100K:元空间无变化,本次不处理类卸载

(2)Full GC 日志样例(危险)

2026-07-17T10:01:00.456+0800:12400.456:[Full GC(Metadata GC Threshold)[PSYoungGen: 6000K->0K(92160K)][ParOldGen: 80000K->78000K(102400K)]86000K->78000K(194560K),[Metaspace: 45000K->44800K(108032K)],0.567890secs]

触发原因Metadata GC Threshold:元空间占用达到 MetaspaceSize 阈值,强制 FullGC

【3】如何判断哪个区域出问题

(1)新生代 Eden 问题

日志特征:

  • 频繁GC (Allocation Failure),YGC 每分钟几十上百次
  • Eden 区 EU 快速打满,每次 YGC 回收大量内存但很快再次占满
    根因:
  1. 对象创建速度过快(接口并发高、循环创建临时对象)
  2. -Xmn新生代内存设置过小
    现象:YGC 频繁,轻微 STW,CPU 小幅升高

(2)老年代 Old Gen 问题(堆内存泄漏)

日志特征:

  1. 每次 YGC 后 OU(老年代已用)持续阶梯上涨
  2. FullGC 次数逐步增多
  3. FullGC 前后老年代内存几乎无下降:ParOldGen:80000K->79500K
  4. 最终抛出Java heap space OOM
    根因:静态集合、ThreadLocal、缓存未清理,对象长期存活晋升老年代无法回收

(3)元空间 Metaspace 问题(元空间泄漏)

日志关键字:

  • FullGC 触发原因:Metadata GC Threshold/Metaspace allocation failure
  • GC 日志中Metaspace: 42000K->41980K,回收几乎无释放
  • jstat -gc MU 持续单向上涨,类卸载日志极少或无
    根因:热部署、CGLIB 动态代理、自定义 ClassLoader 未释放,类无法卸载
    最终报错:java.lang.OutOfMemoryError: Metaspace

(4)补充区分特征

表格

故障区域GC 核心特征jstat 表现
新生代YGC 极其频繁,OU 稳定EU 快速起落,OU 平稳
老年代泄漏FGC 持续增加,FullGC 回收空间极少OU 只涨不跌
元空间泄漏大量 FullGC 由 Metadata 阈值触发MU 持续上涨,无回落

【三】堆内存监控:轻量分析方案(不生成 dump 大文件)

方案 1:jstat 持续监控(生产首选,零 IO、低性能损耗)

组合命令实时观测三代内存变化,无需快照:

# 每1秒输出GC使用率+元空间状态jstat-gcutil125681000# 单独监控元空间jstat-gcmetacapacity125681000

适用场景:线上持续观测内存走势,判断是否泄漏。

方案 2:jmap -heap 查看分代配置与实时占用

瞬时打印新生代、老年代、元空间总容量、当前使用量,无文件生成,秒级返回。

方案 3:jmap -histo 轻量对象统计(定位泄漏对象)

导出所有对象数量与内存占用,文件远小于 hprof 快照,快速定位自定义业务大对象:

jmap-histo12568>histo.log# 过滤业务自定义类grep"com.yourpackage"histo.log

方案 4:可视化轻量工具 Arthas(阿里开源,推荐线上)

无需重启进程,attach 后实时监控分代、GC、对象,无大 dump 文件
核心命令:

# 查看堆各区域实时占用memory# 实时GC监控gc-i1000# 统计对象创建排行heapdump--live/tmp/tmp.hprof# 可选只dump存活对象,文件更小

优势:支持在线查看类加载数量、元空间占用、线程状态,不用记忆大量 Linux 原生命令。

【四】Heap Dump(hprof)快照是否包含元空间数据?

结论:标准jmap -dump堆快照不包含完整元空间类元数据

细分说明

  1. hprof 包含内容:
    • Java 堆所有对象、数组、Class 实例(堆中的java.lang.Class对象)
    • 静态变量、ThreadLocal、字符串常量池、对象引用链
  2. hprof不包含
    • 元空间底层原始类元数据(字节码、运行时常量池、方法表、字段信息)
    • ClassLoader、类加载卸载统计、本地内存 Chunk 分配信息

如果要完整分析元空间问题配套方案

  1. 轻量排查元空间:jstat -gcmetacapacity+jmap -clstats+ GC 日志-XX:+TraceClassUnloading
  2. 深度分析类加载泄漏:
    • 导出 GC 日志看类卸载记录
    • 使用 Arthasclassloader命令查看各加载器加载类数量
    • MAT 打开 hprof 可查看堆内 ClassLoader 实例,但看不到元空间底层字节码数据

补充:OOM 元空间时仅靠 hprof 不够

元空间泄漏根源是 ClassLoader 无法回收,hprof 只能看到堆里的 ClassLoader 对象,完整判断必须配合 GC 日志、类加载统计、jstat 元空间监控三者结合。

【五】完整排查流程总结

  1. jps -l获取 Java PID
  2. CPU 飙高:top -H定位高 CPU 线程 →jstack匹配线程栈,区分业务死循环 / GC 线程消耗
  3. 频繁 FullGC / 内存缓慢上涨:jstat -gcutil 1000持续观测新生代、老年代、元空间走势
  4. 怀疑堆泄漏:jmap -histo轻量分析对象,确认泄漏后生成 hprof 快照用 MAT 分析
  5. 怀疑元空间泄漏:查看 GC 日志 Metadata 触发的 FullGC、jstat -gcmetacapacity、类卸载日志
  6. OOM 事后复盘:启用-XX:+HeapDumpOnOutOfMemoryError自动生成 dump,同时留存完整 GC 日志

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询