java中用jstack定位死锁和cpu飙高问题,关键是识别阻塞源头与执行热点:先通过top和jstack获取实时线程快照,再依deadlock关键字验证死锁闭环,结合nid和runnable状态定位cpu热点,辅以perf或async-profiler深入分析。

Java中通过jstack日志定位死锁和CPU飙高问题,核心是读懂线程状态、锁持有关系与执行热点。关键不在于堆栈本身有多长,而在于识别“阻塞源头”和“活跃热点”。
一、用jstack捕获有效线程快照
确保获取的是问题现场的实时快照,而非空闲状态:
- 对疑似高CPU进程(如PID=12345),先用top -Hp 12345查出占用CPU最高的线程ID(十进制),转为十六进制(如29217 → 0x7221)
- 执行jstack 12345 > jstack.log,建议连续采样2~3次(间隔3~5秒),便于比对线程状态变化
- 若JVM启用了-XX:+UseContainerSupport(如Docker环境),需确认宿主机PID与容器内PID一致,必要时用nsenter进入容器再执行jstack
二、快速识别死锁:看DEADLOCK关键字+线程等待链
jstack会自动检测并标出死锁,但需人工验证是否真实业务死锁:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 搜索"Found one Java-level deadlock",下方会列出互相等待的线程及锁对象(如java.util.concurrent.locks.ReentrantLock$NonfairSync@1a2b3c4d)
- 检查每个线程的"waiting to lock"和"locked"行,确认是否形成闭环(A等B持有的锁,B等A持有的锁)
- 注意区分"java.lang.Thread.State: BLOCKED (on object monitor)"(真阻塞)和"WAITING (parking)"(如await、join、LockSupport.park,未必是死锁)
三、定位CPU飙高线程:结合线程栈+原生线程ID+代码热点
CPU高通常源于某类线程持续执行(如无限循环、密集计算、频繁GC),而非单纯“卡住”:
- 在jstack输出中找到对应十六进制线程ID的"nid=0x7221"段,看其java.lang.Thread.State是否为RUNNABLE,且栈顶是业务方法(非sleep/wait/io阻塞)
- 重点观察栈顶几行:若反复出现HashMap.get/put、String.indexOf、正则匹配、JSON序列化、Stream.forEach等,可能是算法复杂度高或数据量突增所致
- 配合perf top -p 12345(Linux)或async-profiler可进一步确认热点在JVM还是本地方法(如JNI、Inflater)
四、辅助判断:关注常见陷阱模式
有些现象看似正常,实为隐患:
- 大量线程处于"TIMED_WAITING (parking)":可能是线程池满后任务被拒绝,或CompletableFuture未设置超时,导致堆积
- 多个线程重复打印同一段业务栈(如Controller→Service→Mapper):说明请求处理慢,可能由慢SQL、远程调用未设timeout、锁粒度大引起
- Finalizer线程长期RUNNABLE:表明有大量对象重写了finalize()且未及时回收,已废弃但仍有遗留代码
分析jstack本质是逆向还原线程行为逻辑。死锁看“谁在等谁”,CPU高看“谁在不停干啥”。多一次采样、多一行栈追踪,往往就能避开重启救火。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










