【bugfix】记一次CPU飙高的排查经历

目录

1.定位进程

2.定位线程

3.线程虚拟机栈分析

4.解决方案

4.1 升级JDK

4.2 使用ConcurrentHashMap


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

并发容器有锁控制,不会有多个线程并发修改链表,不会形成回路。

发布了121 篇原创文章 · 获赞 111 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/105136798