Java工具-利用jstack定位Java線程堆棧信息(一)

一、概述

??JDK本身提供了許多方便的性能調(diào)優(yōu)及監(jiān)控的小工具,這些小工具雖然沒有說是官方的標(biāo)準(zhǔn)工具,但自從Java誕生這么多年,這些工具一直在被人使用,不得不說這是JDK給開發(fā)者的福利,這些工具包含但不僅限于:jstack、jps、jmap、jConsole、jstat等等。
?由于前些時候用到了jstack這個工具,所以今天來簡單總結(jié)下jstack的使用。

二、jstack使用

首先,jstack會生成JVM當(dāng)前時刻的線程快照,然后我們可以通過它查看某個Java進(jìn)程內(nèi)的線程堆棧信息,通常來說,當(dāng)線上CPU使用率較高的時候,我們可以通過jstack查詢占用CPU較高的一些線程的使用情況,比如發(fā)生了死鎖,線程阻塞等相關(guān)操作。一般情況下,jstack會配合其他命令一塊進(jìn)行操作,比如top,ps等命令。我們先來看下使用jstack命令的一些步驟。

1. 查詢占用CPU最高的進(jìn)程

首先,我們通過top命令查詢當(dāng)前CPU的使用情況,top命令前面已詳細(xì)說過,這里不多說,直接輸入命令:top

這里我們可以看到各個進(jìn)程對CPU的使用占比,并且可以根據(jù)top相關(guān)命令進(jìn)行排序。

2. 查詢該進(jìn)程下占用CPU最高的線程

接下來我們可以根據(jù)進(jìn)程id查詢該進(jìn)程下占用CPU比較高的線程,輸入命令:top -Hp PID,其中PID是上面的那個進(jìn)程id,比如我們這個的:top -Hp 12386

如果對PS命令比較熟的,還可以直接通過:ps H -eo pid,tid,pcpu | sort -n -k 3 | tail -10 來定位到相關(guān)線程:

如果想看下線程的詳細(xì)信息,還可以通過:cat /proc/進(jìn)程號/task/線程號/status來查看。如果需要查詢該進(jìn)程下所有的線程,我們還可以通過pstree命令,以樹的形式展示:

3. 使用jstack獲取對應(yīng)的線程信息

這里我們獲取到了對應(yīng)的線程id,由于jstack輸出中的線程id是16進(jìn)制的,所以需要先將線程id轉(zhuǎn)為16進(jìn)制:

然后使用jstack命令查看對應(yīng)的堆棧信息:jstack $pid|grep -A N $nid,其中此處的pid指的是進(jìn)程號,而nid指的是該進(jìn)程下占用最多的線程號,比如:jstack 12386|grep -A 30 30c6

這里來簡單看下各數(shù)據(jù)項的含義:

  • 最前面的是線程名稱;當(dāng)我們通過Thread來創(chuàng)建線程的時候,線程會被命名為Thread-(序號);當(dāng)我們使用連接池通過ThreadFactory來創(chuàng)建線程時,線程會被命名為 pool-(序號)-thread-(序號)
  • tid,指的是線程id;
  • prio,指的是線程優(yōu)先級,值越大優(yōu)先級越高;
  • os_prio,表示的對應(yīng)操作系統(tǒng)線程的優(yōu)先級,由于并不是所有的操作系統(tǒng)都支持線程優(yōu)先級,所以可能會出現(xiàn)都為0的情況;
  • nid,操作系統(tǒng)映射的線程id,每一個java線程都有一個對應(yīng)的操作系統(tǒng)線程;
  • java.lang.Thread.State:RUNNABLE,當(dāng)前線程的狀態(tài);如果是WATTING狀態(tài),后面會跟上調(diào)用哪個方法導(dǎo)致的watting狀態(tài);
  • 另外線程是否持有鎖信息等,如果持有鎖,則是locked<>;而如果是正在等待獲取鎖,則是 wating for <>;

grep命令中的 -A 表示顯示后面若干行,前面也已經(jīng)學(xué)習(xí)過這個命令,不多說了。本例中可以看到的是Kafka一直在消費(fèi)數(shù)據(jù),正常來說,除了確實(shí)是那種需要密集計算的應(yīng)用之外,一個應(yīng)用的CPU都不會太高,除非出現(xiàn)了一些其他的異常情況。

另外,我們還可以將該進(jìn)程的相關(guān)線程信息導(dǎo)出到stack.dump文件中,然后我們可以在這個文件中查看每個線程的具體狀態(tài):jstack pid(進(jìn)程pid)>stack.dump,這里就不多說了。

三、jstack語法

jstack本身的使用并不復(fù)雜,我們來看下它的一些參數(shù):

deploy@127.0.0.1:~$ jstack -help
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 message

其中參數(shù)文檔已經(jīng)說的很明確了,這里再說下:

  • -l 會打印出額外的鎖信息,在發(fā)生死鎖時可以用jstack -l pid來觀察鎖的持有情況;
  • -m 不僅會輸出Java堆棧信息,還會輸出C/C++堆棧信息(比如Native方法);

比如說:

deploy@127.0.0.1:~$ jstack -l 27075 | grep -A 50 6a15
"pool-1-kafka@thread-1" #45 prio=5 os_prio=0 tid=0x00007f63b1f18000 nid=0x6a15 runnable [0x00007f6324c6b000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
    ...
    - locked <0x000000072028f2a8> (a sun.nio.ch.Util$2)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    ...
    at org.apache.kafka.clients.consumer.KafkaConsumer.pollOnce(KafkaConsumer.java:1096)
    at org.apache.kafka.clients.consumer.KafkaConsumer.poll(KafkaConsumer.java:1043)
    at ****.KafkaConsumerWorker.run(KafkaConsumerWorker.java:39)
    ...
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - <0x000000072012cbb0> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"Curator-PathChildrenCache-0" #44 daemon prio=5 os_prio=0 tid=0x00007f63b1ce4800 nid=0x6a14 waiting on condition [0x00007f6324538000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000007201df280> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - None

這里可以看到當(dāng)前線程類型:daemon ,當(dāng)前線程正在等待獲取鎖 0x00000007201df280,并且當(dāng)前線程沒有持有鎖;不過我們更多的是關(guān)注用戶線程。

這里,涉及到了Locked ownable synchronizers,這里其實(shí)有點(diǎn)沒看懂,如果表示是該線程鎖定的資源的話,那上面locked就會展示了;在stackoverflow上看了下,"ownable synchronizers" list只會出現(xiàn)write lock,而不會出現(xiàn)read lock;然后看了下官方文檔:

An ownable synchronizer is a synchronizer that may be exclusively owned by a thread and uses AbstractOwnableSynchronizer (or its subclass) to implement its synchronization property. ReentrantLock and ReentrantReadWriteLock are two examples of ownable synchronizers provided by the platform.

不過還是沒有看太懂,以后看到的時候再補(bǔ)充吧(TODO)。
有關(guān)Locked ownable synchronizers的說明,可以參考:what-is-locked-ownable-synchronizers-in-thread-dump-stackoverflow.com

四、jstack注意事項

在使用jstack之前,我們肯定是需要了解線程的幾種狀態(tài)的,而在這里我們需要關(guān)注的一般有如下幾種:

  • RUNNABLE,線程處于執(zhí)行中;一般指該線程正在執(zhí)行狀態(tài)中,該線程占用了資源,正在處理某個請求,或者正在進(jìn)行資源的操作等;
  • BLOCKED,線程阻塞;
  • WAITING,線程正在等待中;Waiting on condition(等待資源或條件),Waiting for monitor entry(waiting to lock ,等待獲取鎖);
  • Deadlock ,死鎖;一個線程鎖了某個資源A,等待另一個資源B;而另一個線程恰好鎖了這個被等待的資源B,在等待資源A;

另外,還需要注意:

  • 如果我們要通過dump來查看問題的話,那一次dump可能不足以確認(rèn)問題,可以產(chǎn)生多次 dump信息,如果每次 dump都指向同一個問題,我們基本就可以確定問題的所在。
  • 使用jstack的時候,需要對應(yīng)的服務(wù)器權(quán)限,需要注意,如果沒有權(quán)限的話,會提示你不允許的操作。

而有關(guān)jstack更多操作,可以參考以下鏈接:
JVM性能分析工具jstack介紹
JVM故障分析系列之四:jstack生成的Thread Dump日志線程狀態(tài)
jstack線程dump輸出狀態(tài)解釋

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容