尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Java 线上排查标准手册:CPU 飙高、内存泄漏、接口慢,jstack/jmap/jstat 命令速查

Java 线上排查标准手册:CPU 飙高、内存泄漏、接口慢,jstack/jmap/jstat 命令速查
📅 发布时间:2026/6/29 18:14:17

Java 线上排查标准手册:CPU 飙高、内存泄漏、接口慢,jstack/jmap/jstat 命令速查

目录

  • 一、前置准备:看一眼 top 确定方向
  • 二、CPU 飙高排查
  • 三、内存泄漏 / OOM 排查
  • 四、接口响应慢排查
  • 五、死锁排查
  • 六、线程数异常排查
  • 七、JVM 参数推荐
  • 八、命令速查总表

一、前置准备:看一眼 top 确定方向

所有排查的第一步——确认问题类型:

# 找到 Java 进程 PID jps -l # 或 pgrep -f java # 看 CPU 和内存 top -p <java_pid>
PID USER %CPU %MEM TIME+ COMMAND 12345 root 95.2 8.3 12:30.15 java

决策树:

top 看到什么 → 走哪条路径 ───────────────────────────────── %CPU > 80% 且持续 → 第二章「CPU 飙高」 %MEM 持续上涨 → 第三章「内存泄漏」 %CPU/%MEM 正常但慢 → 第四章「接口慢」 线程数异常增加 → 第六章「线程异常」

二、CPU 飙高排查

2.1 找到吃 CPU 的线程

# -H:显示线程级别 # -p:指定进程 top -Hp <java_pid>

输出示例:

PID USER %CPU %MEM TIME+ COMMAND 12399 root 92.1 0.1 5:30.15 java ← 这个线程吃了 92% CPU 12345 root 1.2 8.3 12:30.15 java 12401 root 0.5 0.1 0:10.22 java

记下最忙线程的 PID(这里是 12399)。

2.2 PID 转十六进制

printf "%x\n" 12399 # 输出:306f

2.3 jstack 定位代码行

# 打印线程栈,grep 过滤目标线程 jstack <java_pid> | grep -A 30 "0x306f"

输出解读:

"http-nio-8080-exec-5" #42 daemon prio=5 os_prio=0 tid=0x... nid=0x306f runnable java.lang.Thread.State: RUNNABLE at com.example.OrderService.queryOrders(OrderService.java:87) at com.example.OrderController.list(OrderController.java:23) at javax.servlet.http.HttpServlet.service(HttpServlet.java:...) ...
栈顶出现什么含义下一步
你的业务代码 + 行号死循环或密集计算审查该行代码逻辑
HashMap.get()/ArrayList.grow()集合操作不当检查数据量或 hash 冲突
GC相关类内存不足触发频繁 GC转到第三章
SocketInputStream.read()等待网络 I/O其实不是 CPU 问题,看第四章

2.4 持续采样确认

# 每 3 秒采样一次,共 5 次,看是否卡在同一行 for i in $(seq 1 5); do echo "=== Sample $i ===" jstack <java_pid> | grep -A 5 "0x306f" sleep 3 done

5 次都在同一行→ 死循环或密集计算,改代码。
每次在不同行→ 高并发请求打进来,考虑扩容或限流。


三、内存泄漏 / OOM 排查

3.1 jstat 看 GC 概况

# 每 1 秒输出一次,共 10 次 jstat -gc <java_pid> 1000 10

输出列说明:

S0C S1C S0U S1U EC EU OC OU MC MU Survivor区 Survivor区 Eden区 Eden区 老年代容量 老年代使用量 元空间 容量 使用量 容量 使用量

重点关注:

jstat -gc <java_pid> 1000 5 | awk ' NR==1 {print "老年代使用率 | YGC | YGCT | FGC | FGCT | GCT"} NR>1 { ou=$8; oc=$7; rate=(oc>0 ? ou/oc*100 : 0); printf " %.1f%% | %4d | %5.2f | %3d | %5.2f | %5.2f\n", rate,$9,$10,$11,$12,$13 }'
指标正常值异常值含义
老年代使用率 (OU/OC)< 70%> 90%即将 OOM
FGC(Full GC 次数)每小时 < 5每几分钟一次内存泄漏导致频繁 GC
FGCT(Full GC 耗时)< 2s> 10sGC 停顿严重影响响应
YGC(Young GC 次数)视吞吐而定每秒几十次新生代太小或对象分配过快

3.2 jmap -histo 快速定位嫌疑类

# 按实例数降序排列,看前 30 个类 jmap -histo <java_pid> | head -30

输出:

num #instances #bytes class name 1: 1827364 438567360 [C ← char[](通常是 String) 2: 1523456 365629440 java.lang.String 3: 234567 56194080 java.util.HashMap$Node 4: 123456 29629440 com.example.dto.OrderDTO ← ⚠️ 你的 DTO 5: 98765 23703600 java.util.ArrayList

分析思路:

  • char[]和String排前两名正常—— 任何 Java 应用都这样
  • 你的业务类(DTO、Entity)突然排进前 10—— 大概率是内存泄漏点
  • HashMap$Node异常多—— 某个 Map 在不断增长却没清理

3.3 jmap -dump 生成 heap dump

# 生成 heap dump 文件 jmap -dump:format=b,file=/tmp/heap_$(date +%Y%m%d_%H%M%S).hprof <java_pid>

⚠️ dump 期间 JVM 会暂停(STW),线上慎用。优先用-histo快速诊断。

3.4 让 JVM 在 OOM 时自动 dump

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/heap.hprof -XX:OnOutOfMemoryError="kill -9 %p" # OOM 后自动重启(可选)

3.5 内存泄漏常见根因

根因表现jmap -histo 特征
ThreadLocal 未清理老年代缓慢增长业务 DTO 数量与请求量不成比例
静态集合无限追加老年代持续增长ArrayList或HashMap实例数少但#bytes大
数据库连接未关闭连接池耗尽不会 OOM,但线程会 WAITING
第三方库 bug特定类的实例异常多搜可疑的第三方类名

四、接口响应慢排查

4.1 看线程状态分布

# 统计各状态的线程数量 jstack <java_pid> | grep "java.lang.Thread.State" | sort | uniq -c | sort -rn

输出示例:

127 java.lang.Thread.State: WAITING (parking) 45 java.lang.Thread.State: TIMED_WAITING (parking) 12 java.lang.Thread.State: BLOCKED 8 java.lang.Thread.State: RUNNABLE
状态多意味着什么
BLOCKED锁竞争——有线程持有锁不放,其他线程干等
WAITING (parking)在等外部条件——数据库连接、Redis 响应、下游 HTTP
TIMED_WAITING超时等待——可能是连接池耗尽的前兆
RUNNABLE 少线程都在等,不在干活——排查外部依赖

4.2 找到阻塞源头

# 看 BLOCKED 状态的线程在等哪个锁 jstack <java_pid> | grep -A 30 "BLOCKED"

关键信息:

- waiting to lock <0x00000007a1b2c3d8> (a java.util.HashMap) - locked <0x00000007a1b2c3d8> (a java.util.HashMap) at ...

持有锁的线程在干什么?如果在做 I/O,那就是锁粒度过大。

4.3 检查数据库连接池

# 看有多少线程在等 HikariCP 连接 jstack <java_pid> | grep -c "HikariPool.*getConnection"

如果这个数字接近maximum-pool-size,连接池满了。


五、死锁排查

5.1 jstack 自动检测死锁

jstack <java_pid> | grep -A 50 "deadlock"

jstack 会自动检测死锁并在输出末尾列出。如果存在死锁,输出会明确标注:

Found one Java-level deadlock: ============================ "Thread-1": waiting to lock monitor 0x00007f... (object 0x00000007a1b2c3d8, a java.lang.Object), which is held by "Thread-2" "Thread-2": waiting to lock monitor 0x00007f... (object 0x00000007a1b2c3e0, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: ===================================================

5.2 如果没有自动检测

# 手动搜 BLOCKED 线程及其等待的锁 jstack <java_pid> | grep -B 2 -A 10 "waiting to lock"

六、线程数异常排查

6.1 查看总线程数

# 方式一 jstack <java_pid> | grep "^""" | wc -l # 方式二:查看线程列表 jcmd <java_pid> Thread.print | grep "^""" | wc -l

6.2 按线程名分组统计

jstack <java_pid> | grep "^""" | awk -F'"' '{print $2}' | \ sed 's/-[0-9]*$//' | sort | uniq -c | sort -rn | head -20

输出:

200 http-nio-8080-exec 50 SimpleAsyncTaskExecutor 30 scheduling- 10 HikariPool
线程名异常数量可能原因
http-nio-xxx-exec> 200Tomcat 线程池配大了或请求积压
SimpleAsyncTaskExecutor几千@Async没配线程池,每次新建线程
HikariPool> 连接池大小连接池泄露
自定义线程名持续增长线程池没设 max-size 或线程未回收

七、JVM 参数推荐

# 生产环境基线 JVM 参数 java \ -Xms4g -Xmx4g \ # 堆大小,min=max 避免动态扩缩 -XX:+UseG1GC \ # G1 收集器(大堆首选) -XX:MaxGCPauseMillis=200 \ # GC 停顿目标 200ms -XX:+HeapDumpOnOutOfMemoryError \ # OOM 自动 dump -XX:HeapDumpPath=/data/dump/ \ # dump 路径 -XX:+PrintGCDetails \ # GC 日志 -XX:+PrintGCDateStamps \ -Xloggc:/data/logs/gc.log \ -jar app.jar

八、命令速查总表

症状 第一步命令 第二步 第三步 ──────────────────────────────────────────────────────────────────────────── CPU 飙高 top -Hp <pid> printf "%x\n" <tid> jstack <pid> | grep 0x<hex> 定位到代码行 内存上涨 jstat -gc <pid> 1000 10 jmap -histo <pid> jmap -dump:format=b,file=... → 看 OU/OC 使用率 → 找嫌疑类 → 离线分析 Full GC 频繁 jstat -gc <pid> 1000 5 jmap -histo <pid> 同上 → 看 FGC 次数和耗时 → 确认泄漏 接口慢 jstack <pid> | grep jstack <pid> | grep 检查慢 SQL、连接池、 "Thread.State" -A 30 "BLOCKED" 下游 HTTP 超时 → 看状态分布 → 分析阻塞链 死锁 jstack <pid> | grep jstack 输出末尾自动 -A 50 "deadlock" 标注死锁详情 线程数异常 jstack <pid> | grep 按线程名分组统计 "^""" | wc -l → 定位线程泄漏源 OOM 已发生 查看 hs_err_pid.log jmap -dump(如仍在运行) MAT / JProfiler → JVM 崩溃日志 → 分析 dump → 找最大对象

核心原则:先确定症状类型,再走对应路径。不要上来就 jstack 全量 dump 然后人肉分析——"先分类,再深入"比"一把梭"快十倍。

📋文章摘要

本文系统整理了 Java 线上问题排查的标准命令和完整路径。覆盖六大场景:CPU 飙高(top -Hp → printf 转十六进制 → jstack 定位代码行)、内存泄漏(jstat 看 GC → jmap -histo 找嫌疑类 → jmap dump 离线分析)、接口慢(线程状态分布 → 阻塞链分析 → 连接池检查)、死锁、线程数异常、OOM 应急处理。每个场景配有完整命令示例、输出解读表和决策逻辑,文末附 JVM 生产参数推荐和命令速查总表。适合需要不依赖 Arthas 等第三方工具、用 JDK 自带命令完成排查的 Java 后端开发者。

相关新闻

  • 如何用3分钟掌握Calibre繁简中文转换插件:电子书阅读的终极语言解决方案
  • PROTECH 17-108-047211 PCB模块
  • MSPM0窗口看门狗(WWDT)原理、配置与软件设计实战指南

最新新闻

  • 告别繁琐的密钥管理:派大星 API —— 你的终极 AI 模型聚合网关与极致性价比之选
  • BDC理财平台的用户资金减损核验与权益折算方案
  • GPT-5.5 写代码靠谱吗?真实项目测完后我发现这些坑
  • 解决Android自动化输入难题:ADBKeyBoard的3种高效集成方案
  • Python驱动Aspose.Words:精准提取Word文档结构化数据的实战指南
  • 简单理解:单电阻、双电阻、三电阻电机采样的区别

日新闻

  • ENVI5.3.1实战:基于Landsat 8影像的区域无缝镶嵌与精准裁剪
  • 3步完成HS2-HF Patch安装:新手快速打造完美HoneySelect2体验
  • 微信好友检测终极指南:3分钟发现谁已悄悄删除你

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号