直接查看线程栈定位hashmap.resize()或get()中的死循环调用链;用top -h -p找高cpu线程pid,转十六进制后jstack过滤,确认runnable状态及环形链表特征;检查共享hashmap是否未同步。

直接看线程栈,定位到 HashMap.resize() 或 get() 中的死循环调用链,这是最快速有效的切入点。
查占用最高的线程 PID
用 top -H -p java-pid> 找出 CPU 占用最高的线程 ID。注意:-H 表示显示线程,不是进程。记录下那个接近 100% 的线程 PID(比如 12348)。
- 再用 printf "%x\n" 12348 转成十六进制(如 303c),后续匹配堆栈要用
- 别只看整体 CPU,要落到具体线程;多个线程同时卡在 resize 就是强信号
抓线程快照并过滤关键帧
执行 jstack
- 线程状态是否为 RUNNABLE(不是 BLOCKED 或 WAITING)
- 栈顶是否在 HashMap.resize()、transfer()(JDK7)或 treeifyBin()/putVal()(JDK8)
- 是否反复出现 at java.util.HashMap.get(HashMap.java:...) 且调用深度异常深(暗示无限遍历)
确认是否环形链表引发死循环
如果堆栈里看到大量重复的 get → getNode → 遍历链表 → next → next...,基本可断定已形成环形链表。典型表现是:
- 同一个线程 dump 多次,栈帧深度持续增长
- get 操作不返回,也不抛异常,CPU 单核打满
- 问题复现不稳定,但一旦触发就难以自愈,重启才恢复
结合代码验证共享 HashMap 实例
检查报错方法中使用的 Map 是否为静态或单例对象,例如:
- public static Map
cache = new HashMap(); - private final Map
orderMap = new HashMap(); // 作为类成员被多线程共用
只要多个线程能并发调用 put() 或 clear(),且没加锁或未用线程安全容器,风险就存在。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











