天天看點

開發神器,使用Arthas快速定位死鎖與CPU負載過高問題!

作者:二哥學Java

Arthas的大名想必大家都聽過,它是一款線上監控診斷産品,通過全局視角實時檢視應用 load、記憶體、gc、線程的狀态資訊,并能在不修改應用代碼的情況下,對業務問題進行診斷,包括檢視方法調用的出入參、異常,監測方法執行耗時,類加載資訊等。今天我來重點介紹一下如何使用Arthas分析線程方面問題

死鎖問題

死鎖問題是經常會遇到的問題,比如說有時候我們發現應用卡住了,很可能是由于某個線程拿住了某個鎖,并且其他線程都在等待這把鎖造成的。為了排查這類問題,arthas提供了相關功能指令,協助我們快速定位。

對此我們直接使用“thread”指令,輸出線程統計資訊。其中:BLOCKED表示目前阻塞的線程數,使我們重點觀察的保護對象,執行“thread -b”指令相當等同于 " jstack–l| grep -i–E 'BLOCKED | deadlock' ”,找出目前阻塞其他線程的線程,造成死鎖的罪魁禍首。

當執行“thread -b”指令後,結果大緻會是以下效果:

"Thread-34" Id=93 BLOCKED on java.lang.Object@1820d623  owned by "Thread-36" Id=92 
at cn.lhy.test.controller.TestController.lambda$hb$1(TestController.java:65)
- blocked on java.lang.Object@1820d623
- locked java.lang.Object@29064ce5 <---- but blocks 3 other threads! 
at cn.lhy.test.controller.TestController$Lambda$1407/395258860.run(Unknown Source) 
at java.lang.Thread.run(Thread.java:748)
           

注:從上面直接輸出了造成死鎖的線程ID和具體的代碼位置以及目前線程一共阻塞的線程數量:“<----but blocks 3 other threads!”,非常的清晰和便利。

CPU負載過高

我們日常的開發過程中,在生産環境或者正式環境中,可能經常會發現CPU過載占用過高的情況,遇到這種問題,一般來講我們會考慮是線程所引起的,固然采用的是thread指令檢視目前線程資訊以及線程的堆棧。

CPU使用率是衡量系統繁忙程度的重要名額,一般情況下單純的CPU高并沒有問題,它代表系統正在不斷地處理我們的任務,但是如果CPU過高,導緻任務處理不過來,進而引起 load 高,這個是非常危險需要關注的。CPU使用率的安全值沒有一個标準值,取決于你的系統是計算密集型還是IO密集型,一般計算密集型應用CPU使用率偏高load偏低,IO密集型相反。

CPU負載使用率過高是直接反映你的作業系統忙碌工作程度的關鍵一個名額,通常情況下單純的發現CPU使用率過高并不是什麼問題,因為這通常代表你的作業系統正在不斷地操作處理你的所有任務,不過一旦發現CPU負載過高,這使得你的任務就很可能處理不過來,進而可能導緻你的CPU負載過高,這一點是十分危險且必須特别注意的。CPU使用率的安全值并非是一個的固定标準值,而是完全取決于你的系統應用是屬于計算密集型還是IO密集型,通常來說計算密集型的系統應用會引起CPU使用率偏高且load值偏低,而IO密集型的系統應用反之。

如果需要定位CPU負載過高的問題,那麼首先我們需要定位CPU過高負載是由哪些線程所引起的,比如GC線程、或者應用程式線程等,這時最簡單的方法就是通過dashboard看闆查詢到整個程序中所有線程、記憶體、GC等情況,例如下圖所示。

開發神器,使用Arthas快速定位死鎖與CPU負載過高問題!

CPU使用率與Linux中指令top -H -p中對應的線程%CPU類似,統計了目前JVM内各個線程的增量CPU時間與采樣時間間隔的比例。

通過以上資料可以分析到哪些線程占用的CPU使用率較高,如果是GC線程占用CPU過多,則需要考慮相關的如何優化GC機制,例如:降低FullGC的頻率和時長,以及對象記憶體的配置設定大小機制等,具體内容可以參考上一節分析FullGG的内容。如果存在相關的業務代碼過多建立線程或者任務過多等原因,需要多考慮對線程池以及相關代碼方面的優化。

如果針對具體代碼的定位,則采用thread指令導出相關的線程dump檔案進行分析,下面是thread的參數選項,借鑒了官方文檔的内容,如下圖所示。

開發神器,使用Arthas快速定位死鎖與CPU負載過高問題!

thread指令參數選項

再次第二次采樣,擷取所有線程的CPU時間,對比兩次采樣資料,計算出每個線程的增量CPU時間。

線程負載的CPU使用率 = 線程增量CPU的運作時間/采樣線程間隔時間 * 100%

[arthas@35]$ thread -n 3
"arthas-command-execute" Id=24 cpuUsage=70.32% deltaTime=0ms time=36ms RUNNABLE
at java.management@15-ea/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@15-ea/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:485)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(
ThreadCommand.java:206)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.
java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(
AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100
(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$
ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.
handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.
run(ProcessImpl.java:385)
at java.base@15-ea/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask
.run(ScheduledThreadPoolExecutor.java:304)
at java.base@15-ea/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.
java:1130)
at java.base@15-ea/java.lang.Thread.run(Thread.java:832)
"C1 CompilerThread0" [Internal] cpuUsage=0.28% deltaTime=0ms time=1032ms
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=982ms
"C2 CompilerThread0" [Internal] cpuUsage=0.01% deltaTime=0ms time=1021ms
"Reference Handler" Id=2 cpuUsage=0.0% deltaTime=0ms time=2ms RUNNABLE
    at java.base@15-ea/java.lang.ref.Reference.waitForReferencePendingList(Native Method)
    at java.base@15-ea/java.lang.ref.Reference.processPendingReferences(Reference.java:241)
    at java.base@15-ea/java.lang.ref.Reference$ReferenceHandler.run(Reference.java:213)
           

進而可以分析出相關的那些CPU負載過高的線程堆棧,以及分析相關的代碼問題以及原因。

注意:由于計算統計線程自身就會産生相應的開銷,是以會看到的統計線程占了一定的比例,為減少對統計自身的開銷所産生的影響,盡量将采樣間隔延長一點。

繼續閱讀