一次服務器CPU佔用率高的定位分析

現象: 當前項目啓動一段時間,有一個服務致使CPU使用率持續超過30%算法

環境:Windows 7,  CPU: 8核, 內存: 8g內存安全

 

定位過程:線程

啓動項目,查看Java進程ID排序

 

查看Event Processor 的CPU使用狀況,此時基本維持在1%左右:隊列

 

開啓Simulator發送幾包數據,再次查看,發現CPU已經飈上去了:進程

 

關閉Simulator,等待數據都處理完畢,再次查看,CPU並未降下去:內存

 

使用ProcessExplorer查看Event Processor線程的CPU使用狀況,發現線程40160和40156的CPU佔用率持續很高,資源

 

使用jstack -l 35684 打印出Event Process的線程棧,查看線程ID(需轉化成16進制)對應(40160-> 9CE0, 40156-> 9CDC)的線程棧:it

 

能夠看出,線程TaskScheduler_com.delta.atm.services.impl.AutoTTExecutorServiceImpl和TaskScheduler_com.delta.atm.services.impl.AlarmExecutorServiceImpl處於運行狀態,持續打印線程信息發現這兩個線程一直處於RUNNABLE的狀態,此時並未開啓simulator,此線程應該處於睡眠纔對。效率

 

先走讀代碼,查看AlarmServiceImpl,

再查看SchedulerServiceImpl,

 

從下面代碼的角度看,在沒有數據的狀況下takeTask應該只會執行一次,而後休眠,等待喚醒。

 

根據打印的線程棧發現,這兩個線程都長時間運行在takeTask方法,因此猜想這個部分有死循環,並且因爲此機器是8核,1/8=0.125,若是有兩個線程持續While循環,則基本就會佔用25%的CPU資源。

 

加上輸出,打印出全局的count,找到緣由了,線程睡眠的條件是count爲0才睡眠,可是數據都處理完了打印出來的count不爲0因此線程會一直空轉,

 

可是爲何會出現數據處理完count還不爲0的狀況呢?

 

從代碼結果來看alarmService每收到一包數據count就會加1,每取一包數據,count就會減1,並且count是AtomicInteger線程安全類型。

因爲alarmService用的是一個生產者-消費者模型,大概結構以下圖所示,須要一個全局的count來表示全部queue裏面的數據,當count數量爲0的時候Task Schedule則睡眠,不然會持續調度。

 

 

查看當前queue裏面的數據是空的,說明queue裏面的數據已經處理完了,可是count的值卻不是0。

查看下面的代碼找到緣由,這個queue是咱們本身封裝的,每一個site對應一個queue,key值是dataTime,這個值是根據印度的box取的且只有10位,精確到秒,可是simulator每一個站點數據的發送頻率常常會一秒鐘發送多包數據,因此就會有重複的dataTime,也就形成了queue裏面數據被覆蓋了,這個時候queue的總大小就和記錄的count不匹配了。

 

其實這個queue的內部咱們使用的是一個treeMap,因此不容許key值重複,最好的辦法是重寫一個容許key值重複而且可排序的一個隊列,可是考慮到現實環境中每一個站點3分鐘才發一包數據不可能出現每秒多包數據的狀況,並且TreeMap內部用的是紅黑樹效率挺高的,若是本身寫排序算法效率可能不會太好,因此暫時使用判斷作規避,若是同一個站點queue裏面已經有相同的dataTime則直接忽略這一包數據。

 

從新打包啓動查看輸出, count已經降下來了

 

查看CPU使用率以下

 

查看線程狀態,已經處於waiting,問題解決。

相關文章
相關標籤/搜索