JVM调优OOM/CPU过高排查+核心工具全教程

在Java后端开发中,JVM调优是保障系统稳定性的核心能力,内存溢出(OOM)、CPU占用过高是线上最常见的故障,而jps、jstat、jmap、jstack这些原生工具则是排查问题的“标配武器”。本文结合一线实战场景,手把手教你用这些工具定位、解决JVM核心问题,内容通俗易懂,覆盖从排查思路到实操命令的全流程。

一、内存溢出(OOM)排查:从现象到根因

1. OOM常见类型与典型场景

OOM并非单一问题,常见类型包括:

  • java.lang.OutOfMemoryError: Java heap space(堆内存不足,对象无法回收);
  • java.lang.OutOfMemoryError: PermGen space/Metaspace(永久代/元空间不足,类加载过多);
  • java.lang.OutOfMemoryError: GC overhead limit exceeded(GC耗时占比过高,系统无响应)。

2. 排查核心步骤(结合jps+jmap)

步骤1:定位问题进程

先用jps命令找到异常Java进程的PID(进程ID),这是所有操作的基础:

# 查看所有Java进程,输出格式:PID 进程名称
jps -l

示例输出:12345 com.example.Application(12345即为目标PID)。

步骤2:导出堆内存快照

通过jmap导出堆快照(hprof文件),用于后续分析内存占用:

# 导出堆快照,dump.hprof为输出文件,12345为进程PID
jmap -dump:format=b,file=dump.hprof 12345

若进程卡死无法直接导出,可加-F强制导出(慎用,可能影响数据完整性):

jmap -F -dump:format=b,file=dump.hprof 12345

步骤3:分析堆快照

将dump.hprof文件导入MAT(Eclipse Memory Analyzer)或JProfiler,重点查看:

  • 占用内存TOP10的对象类型;
  • 是否存在对象泄漏(如静态集合未释放、数据库连接未关闭);
  • 大对象(如超大字符串、数组)是否合理。

3. 常见解决方案

  • 堆内存不足:调整-Xms(初始堆)、-Xmx(最大堆)参数(如-Xms2g -Xmx4g);
  • 元空间不足:调整-XX:MetaspaceSize、-XX:MaxMetaspaceSize;
  • 内存泄漏:修复代码中未释放的对象引用(如关闭流、清空静态Map)。

二、CPU占用过高排查:快速定位耗资源线程

1. 排查核心思路

CPU过高通常是线程死循环、频繁GC、大量同步锁竞争导致,核心是找到“耗CPU的线程”,再定位对应代码。

2. 实操步骤(结合jps+jstack+top)

步骤1:定位高CPU的Java进程

用top命令找到CPU占比最高的Java进程PID(假设为12345):

top

步骤2:定位进程内高CPU线程

在top中按H显示线程,或直接用top -Hp 12345查看该进程下所有线程的CPU占用,找到高耗CPU的线程ID(假设为12346,十进制)。

步骤3:转换线程ID格式

将十进制线程ID转为十六进制(如12346→303a),可通过printf "%x\n" 12346快速转换:

printf "%x\n" 12346

步骤4:导出线程栈并定位问题

用jstack导出进程的线程栈,过滤目标线程:

# 导出全量线程栈
jstack 12345 > thread.log
# 过滤目标线程(303a为十六进制线程ID)
grep -A 20 303a thread.log

查看输出结果中该线程的调用栈,重点关注:

  • 是否处于死循环(如while(true)未退出);
  • 是否卡在某个方法(如自定义的业务方法、第三方组件方法);
  • 是否存在锁等待(如BLOCKED状态的线程)。

3. 常见解决方案

  • 死循环:修复代码逻辑,增加退出条件;
  • 频繁GC:优化内存使用,减少大对象创建,调整GC策略;
  • 锁竞争:优化同步代码块,改用非阻塞锁(如ConcurrentHashMap)。

三、JVM调优核心工具:jps/jstat/jmap/jstack全解析

1. jps:查看Java进程(基础工具)

作用:列出当前系统的所有Java进程,替代ps -ef | grep java的简化版。
常用参数:

  • -l:显示进程的完整类名/ Jar包路径;
  • -v:显示进程的JVM参数;
  • -m:显示进程启动时传入的参数。
    示例:
jps -lv

2. jstat:监控JVM运行状态(实时监控)

作用:实时查看JVM的GC情况、堆内存使用、类加载统计。
核心命令格式:jstat -[选项] [进程PID] [采样间隔(ms)] [采样次数]
常用选项:

  • -gc:查看GC整体统计(新生代、老年代、GC次数/时间);
  • -gcutil:以百分比显示GC统计(最常用);
  • -class:查看类加载/卸载数量。
    示例:
# 每1秒采样一次,共采样10次,查看GC百分比
jstat -gcutil 12345 1000 10

输出字段解读:

  • S0/S1:Survivor0/1区使用率;
  • E:Eden区使用率;
  • O:老年代使用率;
  • M:元空间使用率;
  • YGC/YGT:新生代GC次数/耗时;
  • FGC/FGT:Full GC次数/耗时。

3. jmap:分析堆内存(离线分析)

作用:导出堆快照、查看堆内存分布、统计对象数量。
常用命令:

  • jmap -heap 12345:查看堆内存配置与使用情况;
  • jmap -histo 12345:统计堆中对象数量与大小(按内存排序);
  • jmap -dump:导出堆快照(前文已详述)。
    示例:
# 查看堆内存配置
jmap -heap 12345
# 统计对象数量(前10行)
jmap -histo 12345 | head -10

4. jstack:分析线程栈(定位线程问题)

作用:导出Java进程的线程栈,排查死锁、线程阻塞、死循环。
常用参数:

  • -F:强制导出(进程无响应时用);
  • -l:显示锁相关信息;
  • -m:显示Java+本地方法栈。
    示例:
# 导出线程栈并检查死锁
jstack -l 12345 | grep -i deadlock

四、调优实战注意事项

  1. 线上调优优先用“非侵入式工具”(jps/jstat/jstack),避免影响业务;
  2. 调整JVM参数后需压测验证,避免盲目调大内存导致OOM;
  3. 堆快照文件可能很大,导出时注意磁盘空间,分析后及时清理;
  4. 所有操作需记录(如参数调整、排查步骤),便于复盘。
posted @ 2026-01-12 17:21  高速de蜗牛  阅读(147)  评论(0)    收藏  举报