1、背景
在服務(wù)器上執(zhí)行某個(gè)任務(wù)時(shí),系統(tǒng)突然運(yùn)行緩慢,top 發(fā)現(xiàn)cpu飆升,一度接近100%,最終導(dǎo)致服務(wù)假死。
2、CPU 使用率怎么計(jì)算?
CPU% = 1 – idleTime / sysTime * 100
3、問題分析
#查看所有進(jìn)程占系統(tǒng)cpu的排序,極大可能排第一的就是自己的java進(jìn)程,pid就是進(jìn)程號(hào)。1、top #查看java進(jìn)程下的所有線程占cpu情況。2、top -Hp 進(jìn)程id #可查看進(jìn)程堆棧信息3、jstack 進(jìn)程號(hào) -F當(dāng)正常輸出的請(qǐng)求不被響應(yīng)時(shí),強(qiáng)制輸出線程堆棧-m如果調(diào)用到本地方法的話,可以顯示C/C++的堆棧-l除堆棧外,顯示關(guān)于鎖的附加信息,在發(fā)生死鎖時(shí)可以用jstack -l pid來(lái)觀察鎖持有情況#查找某進(jìn)程下-》線程id(jstack堆棧信息中的nid)=0xa的線程狀態(tài)4、jstack 進(jìn)程號(hào) | grep 線程id最后可以把對(duì)應(yīng)堆棧信息導(dǎo)入到log中,進(jìn)行分析
jstack命令查看對(duì)應(yīng)進(jìn)程的堆棧信息
3、可能導(dǎo)致的原因
計(jì)算密集型的程序是比較耗 CPU 使用率的1、頻繁GC,訪問量高時(shí),有可能造成頻繁的YGC、甚至FGC。當(dāng)調(diào)用量大時(shí),內(nèi)存分配過快,就會(huì)造成GC線程不停的執(zhí)行,導(dǎo)致CPU飆高2、代碼中有大量消耗CPU的操作,導(dǎo)致CPU過高,系統(tǒng)運(yùn)行緩慢; #例如某些復(fù)雜算法,甚至算法BUG,無(wú)限循環(huán)遞歸等等 3、由于鎖使用不當(dāng),導(dǎo)致死鎖4、序列化與反序列化,后文中舉了一個(gè)真實(shí)的案例,程序執(zhí)行xml解析的時(shí),調(diào)用量增大的情況下,導(dǎo)致了CPU被打滿5、加密、解密6、正則表達(dá)式校驗(yàn),曾經(jīng)線上發(fā)生一次血案,正則校驗(yàn)將CPU打滿。大概原因是:Java 正則表達(dá)式使用的引擎實(shí)現(xiàn)是 NFA 自動(dòng)機(jī),這種引擎在進(jìn)行字符匹配會(huì)發(fā)生回溯(backtracking)7、線程上下文切換、當(dāng)啟動(dòng)了很多線程,而這些線程都處于不斷的阻塞狀態(tài)(鎖等待、IO等待等)和執(zhí)行狀態(tài)的變化過程中。當(dāng)鎖競(jìng)爭(zhēng)激烈時(shí),很容易出現(xiàn)這種情況
4、常見的一些問題(來(lái)源于其他大佬的總結(jié))
4-1.while的無(wú)限循環(huán)會(huì)導(dǎo)致CPU使用率飆升嗎?
是。
首先,無(wú)限循環(huán)將調(diào)用CPU寄存器進(jìn)行計(jì)數(shù),此操作將占用CPU資源。那么,如果線程始終處于無(wú)限循環(huán)狀態(tài),CPU是否會(huì)切換線程?
除非操作系統(tǒng)時(shí)間片到期,否則無(wú)限循環(huán)不會(huì)放棄占用的CPU資源,并且無(wú)限循環(huán)將繼續(xù)向系統(tǒng)請(qǐng)求時(shí)間片,直到系統(tǒng)沒有空閑時(shí)間來(lái)執(zhí)行任何其他操作。
4-2.頻繁的Young GC會(huì)導(dǎo)致CPU占用率飆升嗎?
是。
Young GC本身就是JVM用于垃圾收集的操作,它需要計(jì)算內(nèi)存和調(diào)用寄存器。因此,頻繁的Young GC必須占用CPU資源。
讓我們來(lái)看一個(gè)現(xiàn)實(shí)世界的案例。for循環(huán)從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù)集合,然后再次封裝新的數(shù)據(jù)集合。如果內(nèi)存不足以存儲(chǔ),JVM將回收不再使用的數(shù)據(jù)。因此,如果所需的存儲(chǔ)空間很大,您可能會(huì)收到CPU使用率警報(bào)。
4-3.具有大量線程的應(yīng)用程序的CPU使用率是否較高?
不是。
如果通過jstack檢查系統(tǒng)線程狀態(tài)時(shí)線程總數(shù)很大,但處于Runnable和Running狀態(tài)的線程數(shù)不多,則CPU使用率不一定很高。
我遇到過這樣一種情況:系統(tǒng)線程的數(shù)量是1000+,其中超過900個(gè)線程處于BLOCKED和WAITING狀態(tài)。該線程占用很少的CPU。
但是大多數(shù)情況下,如果線程數(shù)很大,那么常見的原因是大量線程處于BLOCKED和WAITING狀態(tài)。
4-4.對(duì)于CPU占用率高的應(yīng)用程序,線程數(shù)是否較大?
不是。
高CPU使用率的關(guān)鍵因素是計(jì)算密集型操作。如果一個(gè)線程中有大量計(jì)算,則CPU使用率也可能很高。這也是數(shù)據(jù)腳本任務(wù)需要在大規(guī)模集群上運(yùn)行的原因。
4-5.處于BLOCKED狀態(tài)的線程是否會(huì)導(dǎo)致CPU占用率飆升?
不會(huì)。
CPU使用率的飆升更多是由于上下文切換或過多的可運(yùn)行狀態(tài)線程。處于阻塞狀態(tài)的線程不一定會(huì)導(dǎo)致CPU使用率上升。
4-6.如果分時(shí)操作系統(tǒng)中CPU的值us或sy值很高,這意味著什么?
可以使用命令查找CPU的值us和sy值top,如以下示例所示:
us:用戶空間占用CPU的百分比。簡(jiǎn)單來(lái)說,高我們是由程序引起的。通過分析線程堆棧很容易找到有問題的線程。
sy:內(nèi)核空間占用CPU的百分比。當(dāng)sy為高時(shí),如果它是由程序引起的,那么它基本上是由于線程上下文切換。