1. 程式人生 > >如何使用jstack分析執行緒狀態

如何使用jstack分析執行緒狀態

背景

記得前段時間,同事說他們測試環境的伺服器cpu使用率一直處於100%,本地又沒有什麼介面呼叫,為什麼會這樣?cpu使用率居高不下,自然是有某些執行緒一直佔用著cpu資源,那又如何檢視佔用cpu較高的執行緒?


當然一個正常的程式設計師不會寫出上述程式碼,這裡只是為了讓一個執行緒佔用較高的cpu資源。

top命令

在linux環境下,可以通過top命令檢視各個程序的cpu使用情況,預設按cpu使用率排序


1、上圖中可以看出pid為23344的java程序佔用了較多的cpu資源;

2、通過top -Hp 23344可以檢視該程序下各個執行緒的cpu使用情況;


上圖中可以看出pid為25077的執行緒佔了較多的cpu資源,利用jstack命令可以繼續檢視該執行緒當前的堆疊狀態。

jstack命令

通過top命令定位到cpu佔用率較高的執行緒之後,繼續使用jstack pid命令檢視當前java程序的堆疊狀態


jstack命令生成的thread dump資訊包含了JVM中所有存活的執行緒,為了分析指定執行緒,必須找出對應執行緒的呼叫棧,應該如何找?

在top命令中,已經獲取到了佔用cpu資源較高的執行緒pid,將該pid轉成16進位制的值,在thread dump中每個執行緒都有一個nid,找到對應的nid即可;隔段時間再執行一次stack命令獲取thread dump,區分兩份dump是否有差別,在nid=0x246c的執行緒呼叫棧中,發現該執行緒一直在執行JstackCase類第33行的calculate方法,得到這個資訊,就可以檢查對應的程式碼是否有問題。

通過thread dump分析執行緒狀態

除了上述的分析,大多數情況下會基於thead dump分析當前各個執行緒的執行情況,如是否存在死鎖、是否存在一個執行緒長時間持有鎖不放等等。

在dump中,執行緒一般存在如下幾種狀態:

  1. RUNNABLE,執行緒處於執行中

  2. BLOCKED,執行緒被阻塞

  3. WAITING,執行緒正在等待

例項1:多執行緒競爭synchronized鎖


很明顯:執行緒1獲取到鎖,處於RUNNABLE狀態,執行緒2處於BLOCK狀態

  1. locked <0x000000076bf62208>說明執行緒1對地址為0x000000076bf62208物件進行了加鎖;

  2. waiting to lock <0x000000076bf62208> 說明執行緒2在等待地址為0x000000076bf62208物件上的鎖;

  3. waiting for monitor entry [0x000000001e21f000]說明執行緒1是通過synchronized關鍵字進入了監視器的臨界區,並處於”Entry Set”佇列,等待monitor,具體實現可以參考《 深入分析synchronized的JVM實現 》

例項2:通過wait掛起執行緒

static class Task implements Runnable {
    @Override
    public void run() {
        synchronized (lock) {
            try {
                lock.wait();
                //TimeUnit.SECONDS.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

dump結果


執行緒1和2都處於WAITING狀態

  1. 執行緒1和2都是先locked <0x000000076bf62500>,再waiting on <0x000000076bf62500>,之所以先鎖再等同一個物件,是因為wait方法需要先通過synchronized獲得該地址物件的monitor;

  2. waiting on <0x000000076bf62500>說明執行緒執行了wait方法之後,釋放了monitor,進入到”Wait Set”佇列,等待其它執行緒執行地址為0x000000076bf62500物件的notify方法,並喚醒自己,具體實現可以參考《 深入分析Object.wait/notify實現機制 》