目录
1.定位进程
top拿到cpu占用最高进程的进程号pid=14398
2.定位线程
top -H -p pid 特定进程中的线程
top -H -p 14398
找到最高的线程id:29230、29156、29151、29197、29240、29284、29291
转换成hex十六进制:722e、71e4、71df、720d、7238、7264、726b
3.线程虚拟机栈分析
jstack pid | grep threadId
jstack生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。pid是进程id,threadId是上个步骤拿到的线程id
jstack 14398 |grep 722e -A 20 --col
上图就是找到的14398进程的0x722e 线程的栈信息,前几名的线程栈几乎都差不多,都是HashMap的get操作。由于我们的服务运行在JDK1.6下,很自然的联想到HashMap线程不安全,在扩容时并发有可能形成链表回路,导致后续操作遍历链表时有死循环!具体请戳
由此几乎验尸完成,就是由于用了线程不安全的HashMap,并发扩容形成链表环路,导致get操作遍历链表时形成死循环,导致线程一直不能执行结束,一直在抢占CPU时间片,导致CPU资源的消耗过大。
4.解决方案
4.1 升级JDK
我们都知道HashMap在1.8fix了扩容时插入链表的顺序,不会造成回路了,所以可以通过升级JDK
4.2 使用ConcurrentHashMap
并发容器有锁控制,不会有多个线程并发修改链表,不会形成回路。