擷取Java線程轉儲的常用方法
1. 線程轉儲簡介
線程轉儲(Thread Dump)就是JVM中所有線程狀态資訊的一次快照。
線程轉儲一般使用文本格式, 可以将其儲存到文本檔案中, 然後人工檢視和分析, 或者使用工具/API自動分析。
Java中的線程模型, 直接使用了作業系統的線程排程模型, 隻進行簡單的封裝。
線程調用棧, 也稱為方法調用棧。 比如在程式執行過程中, 有一連串的方法調用鍊:
obj1.method2
調用了
obj2.methodB
,
obj2.methodB
又調用了
obj3.methodC
。 每個線程的狀态都可以通過這種調用棧來表示。
線程轉儲展示了各個線程的行為, 對于診斷和排查問題非常有用。
下面我們通過具體示例, 來示範各種擷取Java線程轉儲的工具, 以及使用方法。
2. 使用JDK自帶的工具
我們一般使用JDK自帶的指令行工具來擷取Java應用程式的線程轉儲。 這些工具都在JDK主目錄的bin檔案夾下。
是以, 隻要配置好 PATH 路徑即可。 如果不會配置, 可以參考: JDK環境準備
2.1 jstack 工具
jstack 是JDK内置的一款指令行工具, 專門用來檢視線程狀态, 也可以用來執行線程轉儲。
一般先通過
jps
或者
ps
指令找到Java程序對應的pid, 然後在控制台中通過pid來輸出線程轉儲。 當然, 我們也可以将輸出内容重定向到某個檔案中。
使用jstack工具擷取線程轉儲的基本參數格式為:
jstack [-F] [-l] [-m] <pid>1
下面請看具體的示範:
# 1. 檢視幫助資訊jstack -help12
輸出的内容類似于:
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message123456789101112131415
對應的參數選項是可選的。 具體含義如下:
-
選項, 強制執行線程轉儲; 有時候-F
會假死, 則可以加上jstack pid
标志-F
-
選項, 會查找堆記憶體中擁有的同步器以及資源鎖-l
-
選項, 額外列印 native棧幀(C和C++的)-m
例如, 擷取線程轉儲并将結果輸出到檔案:
jstack -F 17264 > /tmp/threaddump.txt1
使用
jps
指令可以擷取本地Java程序的 pid。
2.2 Java Mission Control
Java Mission Control(JMC)是一款用戶端圖形界面工具, 用于收集和分析Java應用程式的各種資料。
啟動JMC後, 首先會顯示本地計算機上運作的Java程序清單。 當然也可以通過JMC連接配接到遠端Java程序。
可以滑鼠右鍵單擊對應的程序, 選擇 “Start Flight Recording(開始飛行記錄)” 。 結束之後, “Threads(線程)” 頁籤會顯示“線程轉儲”:
2.3 jvisualvm
jvisualvm 是一款用戶端圖形界面工具, 既簡單又實用, 可用來監控 Java應用程式, 對JVM進行故障排查和性能分析。
也可以用來擷取線程轉儲。 滑鼠右鍵單擊Java程序, 選擇“ Thread Dump”選項, 則可以建立線程轉儲, 完成後會在新頁籤中自動打開:
2.4 jcmd
jcmd工具本質上是向目标JVM發送一串指令。 盡管支援很多功能, 但不支援連接配接遠端JVM - 隻能在Java程序的本地機器上使用。
其中一個指令是
Thread.print
, 用來擷取線程轉儲, 示例用法如下:
jcmd 17264 Thread.print1
2.5 jconsole
jconsole 工具也可以檢視線程棧跟蹤。
打開jconsole并連接配接到正在運作的Java程序, 導航到“線程”頁籤, 可以檢視每個線程的堆棧跟蹤:
2.6 小結
事實證明, 可以使用JDK中的很多工具來擷取線程轉儲。 讓我們回顧一下, 并總結它們的優缺點:
-
:擷取線程轉儲最簡單最友善的工具; Java 8之後可以使用 jcmd 工具來替代;jstack
-
:增強的JDK性能分析和問題診斷工具。 用這款工具進行性能分析的開銷非常低。jmc
-
:輕量級的開源分析工具, 圖形界面非常棒, 還支援各種強悍的功能插件。jvisualvm
-
: 非常強大的本地工具, 支援Java 8及更高版本。 內建了多種工具的作用, 例如: 捕獲線程轉儲(jstack), 堆轉儲(jmap), 檢視系統屬性和檢視指令行參數(jinfo)jcmd
-
:也可以用來檢視線程棧跟蹤資訊。jconsole
3. 使用Linux指令
在企業應用伺服器中, 出于安全原因, 可能隻安裝了 JRE。 這時候沒法使用這些JDK内置的工具。
但還是有辦法擷取線程轉儲。
3.1 使用 kill -3
指令
kill -3
在Unix/Linux之類的系統中, 可以使用
kill
指令擷取線程轉儲, 底層實作原理, 則是通過系統調用
kill()
将信号參數發送給程序。 這裡需要發送的是
-3
信号。
一般先通過
jps
找到JAVA程序對應的pid,
kill -3
使用示例如下:
kill -3 172641
3.2 Ctrl + Break
(Windows)
Ctrl + Break
在Windows作業系統的指令行視窗中, 可使用組合鍵
Ctrl + Break
來擷取線程轉儲。 當然, 需要先導航至啟動Java程式的控制台視窗, 然後同時按下
CTRL
鍵和
Break
鍵。
需要注意的是, 某些鍵盤是沒有 “
Break
” 鍵的。
在這種情況下, 可以組合使用
CTRL
,
SHIFT
, 以及
Pause
鍵。
這兩個指令都可以将線程轉儲列印到控制台。
4. 通過程式設計方式使用ThreadMxBean
JMX技術支援各種各樣的花式操作。 可通過
ThreadMxBean
來執行線程轉儲。
示例代碼如下:
private static String threadDump(boolean lockedMonitors, boolean lockedSynchronizers) {
StringBuffer threadDump = new StringBuffer(System.lineSeparator());
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
for(ThreadInfo threadInfo : threadMXBean.dumpAllThreads(lockedMonitors, lockedSynchronizers)) {
threadDump.append(threadInfo.toString());
}
return threadDump.toString();}12345678
上面代碼做的事情很簡單, 先通過
ManagementFactory
擷取
ThreadMxBean
對象。
方法的布爾參數
lockedMonitors
和
lockedSynchronizers
, 表示是否導出持有的同步器和管程鎖。
5. 總結
我們通過具體示例展示了擷取線程轉儲的各種方法。
首先介紹的是各種JDK内置工具,
然後讨論了指令行方式,
最後介紹了JMX程式設計的方式。
完整的示例代碼請參考 GitHub倉庫 。