天天看點

擷取Java線程轉儲的常用方法

擷取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

     選項, 會查找堆記憶體中擁有的同步器以及資源鎖
  • -m

     選項, 額外列印 native棧幀(C和C++的)

例如, 擷取線程轉儲并将結果輸出到檔案:

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中的很多工具來擷取線程轉儲。 讓我們回顧一下, 并總結它們的優缺點:

  • jstack

    :擷取線程轉儲最簡單最友善的工具; Java 8之後可以使用 jcmd 工具來替代;
  • jmc

    :增強的JDK性能分析和問題診斷工具。 用這款工具進行性能分析的開銷非常低。
  • jvisualvm

    :輕量級的開源分析工具, 圖形界面非常棒, 還支援各種強悍的功能插件。
  • jcmd

    : 非常強大的本地工具, 支援Java 8及更高版本。 內建了多種工具的作用, 例如: 捕獲線程轉儲(jstack), 堆轉儲(jmap), 檢視系統屬性和檢視指令行參數(jinfo)
  • jconsole

    :也可以用來檢視線程棧跟蹤資訊。

3. 使用Linux指令

在企業應用伺服器中, 出于安全原因, 可能隻安裝了 JRE。 這時候沒法使用這些JDK内置的工具。

但還是有辦法擷取線程轉儲。

3.1 使用 

kill -3

 指令

在Unix/Linux之類的系統中, 可以使用 

kill

 指令擷取線程轉儲, 底層實作原理, 則是通過系統調用 

kill()

 将信号參數發送給程序。 這裡需要發送的是 

-3

 信号。

一般先通過 

jps

 找到JAVA程序對應的pid, 

kill -3

 使用示例如下:

kill -3 172641      

3.2 

Ctrl + Break

 (Windows)

在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倉庫 。

繼續閱讀