JAVA服务问题诊断

第一步:定性 --- 确认故障类型

先看监控大盘(Grafana / Prometheus / SkyWalking 等),确认故障属于哪一类:

  • CPU 持续飙高
  • 内存持续增长 / OOM
  • 频繁 Full GC
  • 接口响应慢 / 超时
  • 线程阻塞 / 死锁

定性决定了后续排查方向,避免上来就盲猜。


第二步:定位异常进程

复制代码
# 查看整机资源占用,找到异常 Java 进程 PID
top -c

# 或者只看 Java 进程
jps -l

如服务器4核,可以看到253533的进程cpu使用率高。

第三步:根据故障类型分流排查

场景一:CPU 飙高

复制代码
# 1. 找到进程内哪个线程最耗 CPU
top -H -p <PID>

# 2. 将高 CPU 线程的 TID 转为十六进制
printf "%x\n" <TID>

# 3. 导出线程堆栈,搜索该 nid
jstack -l <PID> > thread.log
grep -A 30 "nid=0x<十六进制TID>" thread.log

判断要点

  • 堆栈停在业务代码且状态为 RUNNABLE → 死循环、复杂计算、正则回溯
  • 堆栈停在 GC 相关方法 → 频繁 GC 导致,转场景二
  • 大量线程 BLOCKED → 锁竞争,查 waiting to locklocked

💡 关键技巧:间隔 3~5 秒连续抓 3 次 jstack,如果某线程 3 次都停在同一行代码,说明确实卡死;如果一直在变,说明只是执行慢。

jstat -gcutil 是 Java 虚拟机(JVM)中用于监控垃圾回收(GC)状态的命令行工具,其输出以百分比形式展示各内存区域的使用率,便于快速判断 JVM 内存健康状况。该命令不会触发 GC,也不会挂起应用,非常适合线上环境实时观察。

各列含义及正常范围参考如下:

  • S0 / S1(Survivor 区使用率):两个 Survivor 区交替使用,正常情况下一个为 0%,另一个在 0%~100% 之间波动。若两者同时高或长期为 0%,可能 Survivor 区配置过小或对象晋升过快。
  • E(Eden 区使用率):正常应在 0%~95% 之间波动。若持续 >95% 且频繁 Young GC,说明对象分配速率过高或 Eden 区过小。
  • O(老年代使用率):健康范围通常在 30%~70%。超过 70% 需警惕,超过 85% 且持续上升,极可能即将触发 Full GC。
  • M(Metaspace 使用率):应稳定在 80%~95%。若长期 >90% 且持续增长,可能存在类加载泄漏或元空间配置不足。
  • CCS(压缩类空间使用率):与 Metaspace 相关,通常与 M 列趋势一致,若异常升高也需关注类加载问题。
  • YGC / YGCT(Young GC 次数与总耗时):次数随运行时间增长属正常,单次耗时一般 <0.01s 为良好。若 YGCT 增长过快,说明 Young GC 频繁或效率下降。
  • FGC / FGCT(Full GC 次数与总耗时):理想状态为 0。若 FGC > 0 且 FGCT 持续增长,说明系统已出现内存压力,需立即排查。
  • GCT(总 GC 耗时):等于 YGCT + FGCT,用于评估整体 GC 开销。

示例截图


场景二:内存泄漏 / 频繁 Full GC

复制代码
# 1. 实时观察各区域使用率(百分比,直观)
jstat -gcutil <PID> 1000

# 2. 查看堆内存概要
jmap -heap <PID>

# 3. 查看哪些对象实例最多
jmap -histo <PID> | head -20

# 4. 导出堆快照(建议在低峰期执行,会触发 STW)
jmap -dump:live,format=b,file=heap.hprof <PID>

拿到 heap.hprof 后,用 Eclipse MAT 打开分析:

  • 查看 Dominator Tree(支配树),找到占用内存最大的对象
  • 查看 Leak Suspects(泄漏嫌疑),MAT 会自动分析可疑泄漏点
  • 追踪 GC Root 引用链,定位是哪段代码持有了对象导致无法回收

常见根因

  • 静态集合(如 static List)不断往里塞对象
  • 连接 / 流未关闭
  • 线程池未销毁
  • 一次性加载全表数据,未分页
  • 缓存无上限、无过期策略

示例截图


使用mat分析

既然 MemoryLeakBug 占用了 402MB,它内部一定有个巨大的集合或数组。

  • 在 MAT 的 Dominator Tree(支配树) 视图中,找到最大的 com.demo.bugs.MemoryLeakBug 实例。

场景三:服务假死(CPU 不高但接口全超时)

复制代码
# 导出线程堆栈
jstack -l <PID> > thread.log

# 统计线程状态分布
grep "java.lang.Thread.State" thread.log | sort | uniq -c

# 检测死锁
grep -i deadlock thread.log

如果大量线程处于 WAITING / BLOCKED,说明线程都在等资源(数据库连接、Redis、第三方接口、锁等),顺着堆栈找等待的具体资源即可。


第四步:Arthas 加速排查(推荐)

如果线上允许安装,Arthas 可以大幅简化排查:

复制代码
# 安装并启动
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 常用命令
thread -n 3          # 直接看 CPU 最高的 3 个线程堆栈
thread -b            # 一键检测死锁
trace 包名.类名.方法名  # 追踪方法内部各步骤耗时,定位慢接口
watch 包名.类名.方法名  # 监控方法入参、出参、异常
heapdump --live /tmp/heap.hprof  # 导出存活对象堆快照

第五步:日志交叉验证

根据 jstack 堆栈中的代码行号,去业务日志中搜索对应时间点的日志,还原故障触发场景,确认是哪次请求、哪个参数导致的问题。


快速对照表

表格

故障现象 核心命令 常见根因
CPU 飙高 top -H -p + jstack 死循环、正则回溯、频繁 GC
内存持续增长 jstat -gcutil + jmap -histo 静态集合泄漏、连接未关闭
频繁 Full GC jstat -gcutil + MAT 分析 老年代满、大对象、内存泄漏
服务假死 jstack + grep BLOCKED 死锁、锁竞争、外部资源超时
接口响应慢 Arthas trace 慢 SQL、缓存失效、下游超时

排查口诀

top 找进程 → top -H 找线程 → jstack 看代码 → jstat 看 GC → jmap 看内存 → MAT 找泄漏

掌握这套流程,绝大多数线上 Java 故障都能在几分钟内定位到根因。

相关推荐
Sayuanni%32 小时前
SpringBoot 从注解到源码:核心知识点总结
java·spring boot·后端
坚定信念,勇往无前2 小时前
Maven 私有仓库-nexus
java
Minner-Scrapy2 小时前
Scrapy 2.17 源码解析:Scheduler 调度器与磁盘/内存双队列
java·爬虫·python·scrapy·网络爬虫·twisted
晚风醉蝶2 小时前
1-11-奇偶排序-OddEvenSort
java·数据结构·算法
夏天拐跑了西瓜3 小时前
Spring Cloud 微服务实战(三):一个服务挂了,凭什么拖垮整个系统?Sentinel 限流熔断实战
java·spring cloud·微服务
CRMEB定制开发3 小时前
2026年最值得推荐的10大开源商城系统盘点
开发语言·人工智能·开源·商城系统·小程序商城
2601_967264283 小时前
Jetpack Compose 实践指南:从入门到进阶
java·学习
buhuizhiyuci3 小时前
【QT-百日筑基篇】修真世界摸爬滚打多年,终于黄天不负有心人,突破炼器中期——常用控件QWight的属性
开发语言·c++·qt·gui·图形化
m0_587383004 小时前
社区家政系统开发实战:从需求分析到上线部署全指南
java·spring boot·spring·需求分析