1. 功能簡介
jstack
用於打印出給定的 Java進程ID
或遠程調試服務的線程快照。線程快照是 Java 虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環造成的長時間等待。 通過 jstack
查看各個線程的調用堆棧,就可以知道停頓的線程到底在做什麼,或者等待什麼資源。 如果 Java 程序崩潰生成core文件,jstack
工具也可以獲取 core 文件的 Java方法棧和本地方法棧的信息,從而可以直達事故現場分析出程序是如何崩潰的。另外需注意機器上當前登錄用戶和 Java 進程啓動用戶不是同一個時,無法使用該命令
- 需要注意的問題
不同的 JAVA 虛機的線程 DUMP 的創建方法和文件格式是不一樣的,不同的 JVM 版本, dump信息也有差別。在實際運行中,往往一次 dump的信息,還不足以確認問題。建議產生三次 dump信息,如果每次 dump都指向同一個問題,才能確定問題的典型性
2. 使用方式
命令格式 | 參數說明 |
---|---|
jstack [ option ] pid | pid 爲 Java 應用程序的進程號,連接到正在運行的進程 |
jstack [ option ] executable core | executable 爲產生core dump的 java可執行程序,core爲打印出的core文件,該命令可連接到核心文件 |
jstack [ option ] [ server-id@]remote-hostname-or-IP | remote-hostname-or-ip 是遠程debug服務器的名稱或IP,server-id是唯一id,假如一臺主機上有多個遠程debug服務會需求區分 |
Options | 功能 |
---|---|
-F | 當線程沒有響應的時候強制打印棧信息 |
-m | 打印 Java 和 native c/c++ 框架的所有棧信息 |
-l | 長列表. 打印關於鎖的附加信息,例如屬於 java.util.concurrent 的 ownable synchronizers 列表 |
常見的使用方式如下
// 打印指定 Java 進程的棧信息
jstack -l 44295
// 輸出指定 Java 進程的棧信息到 log 文件中
jstack -l 44295 > ~/44295.log
3. 分析注意
3.1 線程狀態
通過 jstack
命令來分析線程的情況,首先要知道線程都有哪些狀態,以下狀態是查看線程堆棧信息時可能會看到的:
RUNNABLE
: 在虛擬機內執行的
BLOCKED
: 受阻塞並等待監視器鎖,通常是多條線程爭奪 synchronized 鎖住資源失敗觸發
WATING
:無限期等待另一個線程執行特定操作
TIMED_WATING
:有時限的等待另一個線程的特定操作,通常由 sleep(n) 及定時任務觸發
TERMINATED
:已退出的
3.2 線程信息的格式
參考 Java 對象鎖的 Monitor 實現,可以知道一個 Monitor
在某個時刻,只能被一個線程擁有,擁有鎖的線程就是 Active Thread
,而其它線程都是 Waiting Thread
,分別在兩個隊列 EntrySet
和 WaitSet
裏面等待。在 EntrySet
中等待的線程狀態是 Waiting for monitor entry
,表示等待獲取鎖;而在 WaitSet
中等待的線程狀態是 in Object.wait()
,表示獲得鎖成功後又釋放,等待被喚醒
一個典型線程信息如下,可以看到在第二行中標出的是線程狀態 WAITING (on object monitor)
,表示在等待獲取 Monitor
對象,而第一行的in Object.wait()
則是等待狀態產生的原因,可以知道是線程當前執行條件不滿足,自己調用了 wait()
方法放棄鎖,等待被 notify()
。而waiting on <no object reference available>
則是當前線程對 Monitor
鎖的操作狀態,表示使用synchronized
申請對象鎖成功後,釋放鎖並在WaitSet
裏面等待被喚醒
“Disposer” #105 daemon prio=10 os_prio=31 cpu=35.51ms elapsed=241580.14s tid=0x00007fa74fb0f800 nid=0x20e87
in Object.wait()
[0x000070000a46c000]
java.lang.Thread.State:WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
-waiting on <no object reference available>
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x00000007493fc428> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:176)
at com.sun.webkit.Disposer.run([email protected]/Disposer.java:122)
at java.lang.Thread.run([email protected]/Thread.java:834)
.
Locked ownable synchronizers:
- None
線程對 Monitor 對象的操作狀態 |
意義 |
---|---|
locked <地址> 目標 | 使用synchronized申請對象鎖成功,監視器的擁有者 |
waiting to lock <地址> 目標 | 使用synchronized申請對象鎖未成功,在EntrySet 隊列等待 |
waiting on <地址> 目標 | 使用synchronized申請對象鎖成功後,釋放鎖在WaitSet 區等待 |
parking to wait for <地址> 目標 | 等待獲取鎖時調用 park() 掛起線程 |
線程狀態描述 | 線程狀態 |
---|---|
runnable | 狀態一般爲RUNNABLE |
in Object.wait() | WaitSet 等待區等待,狀態爲 WAITING 或 TIMED_WAITING |
waiting for monitor entry | EntrySet 進入區等待,狀態爲 BLOCKED |
waiting on condition | WaitSet 等待區等待,線程被 park |
sleeping | 休眠的線程,調用了Thread.sleep() |
3.3 重點信息
搜索關鍵字 | 含義 |
---|---|
Deadlock | 死鎖,重點關注 |
Waiting on condition | 等待資源,重點關注 |
Waiting on monitor entry | 等待獲取監視器,重點關注 |
Blocked | 阻塞,重點關注 |
-
Deadlock
關鍵字
這個一般指多個線程調用時,自己佔用了一部分資源,又需要請求對方佔用的資源導致無限期等待對方釋放資源的情況。這種情況堆棧寫的會很明顯,它會打印Found one Java-level deadlock
,然後指出造成死鎖的線程的信息 -
Blocked
關鍵字
線程狀態是Blocked
,說明線程等待資源超時。此時可查看其waiting to lock <0x0000000xxxxxxx>
信息,在 dump 日誌裏查找這個地址字符串,如發現有大量線程都在等待給這個地址上鎖,可能在代碼級別已經存在衝突的調用。此時如果能在日誌裏找到誰獲得了這個鎖(關鍵字locked <0x0000000xxxxxxx>
),則可以有效找到事故現場 -
Wait on condition
關鍵字
該狀態表示等待資源,或等待某個條件的發生,具體原因需結合堆棧信息來分析。最常見的情況就是線程處於sleep
狀態,等待被喚醒- 如果線程是用戶線程,則證明該線程正在等待資源。一般是大量讀取某資源,且該資源採用了資源鎖的情況下,線程進入等待狀態,等待資源的讀取
- 如果發現有大量的線程都在處在
Wait on condition
,從線程 stack 看正等待網絡讀寫,這可能是一個網絡瓶頸的徵兆,因爲網絡阻塞導致線程無法執行。一種情況是網絡非常忙,幾乎消耗了所有的帶寬,仍然有大量數據等待網絡讀寫;另一種情況也可能是網絡空閒,但由於路由等問題,導致包無法正常到達
-
持續運行的IO
IO
操作可以在RUNNABLE
狀態達成阻塞,例如數據庫死鎖、網絡讀寫。 需要格外注意對IO
線程真實狀態的分析,比如readLine()
造成的IO
阻塞,一般被捕捉到RUNNABLE
的IO
調用都是有問題的