一個Tomcat高CPU佔用問題的定位
前段時間專案(交接過來的)釋出了一個大的版本以後,IDC機器CPU不時會突然飆升,而且是“根本停不下來”的樣子,一上去了就是100%。想來也納悶,雖然發了版本,但沒有太耗CPU的功能,不應該會讓CPU一下子從20%左右飆升到100%,而且是間歇性的,想想也應該是專案本身固有的bug,只不過現在訪問量大了才暴露出來。
先top命令看看是哪個程序當用了大量的CPU,得到pid,繼續top -H -p [pid]找出此程序中CPU佔用排在前頭的活動執行緒,把pid都記錄下來。
接下來jstack -l [pid] > [pid].stack,得到執行緒堆疊,看看各個執行緒都在做什麼事情。根據上面記錄下來的高CPU佔用執行緒的pid在pid.stack檔案中找到對應的執行緒,發現這幾個執行緒都是GC執行緒。這個時候我覺得應該是有記憶體洩露,導致GC過於頻繁,於是jmap檢視記憶體堆疊資訊,並作了一些優化,還對原先的日誌處理方式作了優化。釋出,看起來有效果,可正常運行了幾天了後,又有兩臺機的CPU給飆上去了,看起來根本問題沒有得到解決。
我還是太年輕,太粗心。
又得重新來分析了! 先vmstat來看看機器的情況,發現當前的排隊執行緒有時高達76,低時也有10個以上,已經超出了CPU數。
既然是執行緒問題,還是jstack -l [pid] 來看執行緒堆疊,這次不再草草下結論,細細看每個執行緒都在幹啥!發現有很多執行緒的執行都停留在saveUserAuthDetails方法中某一行:
userAuthnBornTime是一個HashMap物件,更關鍵的是它是static的,也即所有物件公用,這個HashMap物件被用來儲存登陸態的過期時間,很明顯是讀大於寫,但是寫也不少,在訪問量大時,大量的併發執行緒都需要操作這一個物件,導致很多讀操作都處於忙等待狀態。前面的在等待,後面還不停有請求進來,進一步加劇,因此在訪問量突然增加時會直線飆升並且降不下來。
還是因此第一次太果斷!
知道了原因,便得找解決方案了,瞭解Java的java.util.concurrent包自然會想到可以用ConcurrentHashMap來代替HashMap,以提高高併發情況下的效能,ConcurrentHashMap不同於直接使用基於HashMap的同步操作的地方在於它內部同時儲存了資料的多份拷貝,允許多個執行緒併發讀,從而提高效能,更多ConcurrentHashMap相關的介紹可找GOOGLE大神,相關文章太多了!
使用ConcurrentHashMap替換HashMap以後,果然機器CPU都能穩定在20%左右。
友情提示:ConcurrentHashMap對於key和value都不允許為空(記得做判斷,不然出大事)
轉載自jmatrix