必须确认目标进程PID、执行权限与JVM状态:先用jps -l或ps -ef | grep java查PID及启动用户,确保同用户执行或sudo切换;JDK 8u261+需加-XX:+StartAttachListener;进程假死时可用-F强制模式但慎用。

用 jstack 抓取线程快照前必须确认的三件事
不是所有 JVM 进程都能直接 jstack 成功——权限、JVM 版本、进程状态缺一不可。最常见的是普通用户执行时提示 Unable to open socket file: target process not responding or hotspot VM not loaded,这通常意味着目标 JVM 是以其他用户(比如 root 或服务账户)启动的,而你没有权限 attach。
- 先用
ps -ef | grep java确认目标进程 PID 和启动用户,再用sudo -u [user] jstack [pid]切换身份执行 - JDK 8u261+ 默认禁用
jstackattach(出于安全考虑),若报java.lang.RuntimeException: Unable to deduce java executable,需在启动 JVM 时加-XX:+StartAttachListener - 如果进程已卡死但未完全 hang 住(比如还在响应少量请求),
jstack可能超时失败;此时可尝试加-F强制模式(jstack -F [pid]),但会触发全局 safepoint,短暂暂停所有线程
识别死锁:别只看 “Found 1 deadlock” 这行字
jstack 输出里出现 Found 1 deadlock. 是最直观信号,但它只报告“循环等待”的显式死锁(即两个及以上线程互相持有对方需要的 monitor lock)。很多生产问题其实不是这种经典死锁,而是线程长期阻塞在 Object.wait()、LockSupport.park() 或 I/O 上,表现为大量线程处于 WAITING 或 BLOCKED 状态。
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 重点检查每个线程栈顶的
at java.lang.Object.wait(Native Method)或at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)—— 这类等待是否合理?有没有超时机制? - 注意
locked <0x...>和waiting to lock <0x...>的地址是否成对出现,且形成闭环(A 等 B,B 等 C,C 等 A) - 如果没看到
Found 1 deadlock.,但 CPU 持续 100%,优先怀疑自旋、无限 loop 或频繁 GC,而不是死锁
CPU 飙升时,jstack 只是辅助,关键看线程状态分布
jstack 本身不采集 CPU 使用率,它只告诉你线程此刻在做什么。CPU 飙升的真实线索藏在线程状态和栈帧调用链里:RUNNABLE 状态线程过多,尤其是栈顶落在 HashMap.get()、String.indexOf()、正则匹配或 JSON 序列化等 CPU 密集型方法上,才是危险信号。
- 用
jstack [pid] | grep 'java.lang.Thread.State: RUNNABLE' | wc -l快速统计活跃线程数,超过 200 且持续增长,大概率有逻辑异常 - 特别警惕重复出现在多条栈中的方法,比如某个
com.example.util.StringUtils.trim()被上千个线程同时调用,说明可能被高频误用 - 注意 native 方法栈,如
at java.net.SocketInputStream.socketRead0(Native Method)大量出现,可能是连接未关闭导致线程堆积,而非 CPU 问题
抓取时机与频率:一次不够,三次才有对比价值
单次 jstack 快照信息量有限,尤其对瞬态问题(比如几毫秒内完成的锁竞争、短时自旋)。真正定位问题需要至少三次间隔 1–2 秒的快照,观察哪些线程状态/栈帧始终不变,哪些在反复切换。
- 用
for i in {1..3}; do jstack [pid] > jstack_$(date +%s).log; sleep 1.5; done批量抓取,避免手动操作引入时间误差 - 对比时重点关注
threadId(如"http-nio-8080-exec-45")是否在多个快照中都卡在相同方法,而不是只看线程名是否重复 - 如果发现某线程在三次快照中都处于
RUNNABLE且栈顶是java.util.Arrays.sort(),基本可断定它正在处理一个超大集合排序,需要查上游数据量是否失控
-XX:+UseG1GC 后,jstack 中部分线程可能显示 VM Thread 长时间 RUNNABLE,其实是 G1 在做并发标记,并非业务问题。得结合 jstat -gc [pid] 看 GC 日志才能区分。

















