目錄
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
併發容器有鎖控制,不會有多個線程併發修改鏈表,不會形成迴路。