使用jstack分析Java線程

You use the jstack command to print Java stack traces of Java threads for a specified Java process. This command is experimental and unsupported.

官方文檔中,jstack是用于打印指定Java進(jìn)程的線程堆棧跟蹤展姐,我們通常用jstack來(lái)分析死鎖和死循環(huán)等場(chǎng)景。

使用方式及參數(shù)

Usage:
    jstack [-l][-e] <pid>
        (to connect to running process)

Options:
    -l  long listing. Prints additional information about locks
    -e  extended listing. Prints additional information about threads
    -? -h --help -help to print this help message

使用方式是找到Java進(jìn)程id——pid俩莽,jstack pid就能打印出堆棧信息。-l參數(shù)可以打印鎖的信息乔遮,-e可以打印線程的額外信息扮超。

jstack分析死鎖

我們寫(xiě)一個(gè)死鎖的demo DeadlockDemo.java并且運(yùn)行:

Object lock1 = new Object();  
Object lock2 = new Object();  
  
Thread t1 = new Thread(() -> {  
    synchronized (lock1) {  
        System.out.println("Thread 1 acquired lock1");  
        try {  
            sleep(1000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        synchronized (lock2) {  
            System.out.println("Thread 1 acquired lock2");  
        }  
    }  
});  
  
Thread t2 = new Thread(() -> {  
    synchronized (lock2) {  
        System.out.println("Thread 2 acquired lock2");  
        try {  
            sleep(1000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        synchronized (lock1) {  
            System.out.println("Thread 2 acquired lock1");  
        }  
    }  
});  
  
t1.start();  
t2.start();  
  
// 等待兩個(gè)線程執(zhí)行完畢  
try {  
    t1.join();  
    t2.join();  
} catch (InterruptedException e) {  
    e.printStackTrace();  
}  
  
System.out.println("Main thread finished");

使用jps -l查看java程序的端口

jps -l
12406 org.jetbrains.jps.cmdline.Launcher
2156 com.intellij.idea.Main
17119 jdk.jcmd/sun.tools.jps.Jps

命令行執(zhí)行jstack -l -e 12406

2024-02-15 15:30:11
Full thread dump OpenJDK 64-Bit Server VM (17.0.9+9-Ubuntu-120.04 mixed mode, sharing):

第一部分是虛擬機(jī)信息,SMR全稱(chēng)是Safe Memory Reclamation蹋肮,即jvm安全分配的線程:

Threads class SMR info:
_java_thread_list=0x00007f102c001740, length=14, elements={
0x00007f10c0013af0, 0x00007f10c01765a0, 0x00007f10c0177990, 0x00007f10c017e170,
0x00007f10c017f530, 0x00007f10c0180950, 0x00007f10c0182310, 0x00007f10c0183850,
0x00007f10c018ccc0, 0x00007f10c01985d0, 0x00007f10c01c53c0, 0x00007f10c04d9610,
0x00007f10c04e48d0, 0x00007f102c000d20
}

第二部分是線程堆棧信息出刷,堆棧信息里第一行是線程的元信息:

"main" #1 prio=5 os_prio=0 cpu=338.57ms elapsed=91.48s allocated=14953K defined_classes=1526 tid=0x00007f10c0013af0 nid=0x2a95 in Object.wait()  [0x00007f10c7d34000]
  • "main"線程名字
  • #1線程序號(hào)
  • prio優(yōu)先級(jí)
  • os_prioos線程優(yōu)先級(jí)
  • cpu線程獲得cpu的時(shí)間
  • elapsed線程啟動(dòng)后經(jīng)過(guò)的wall clock time
  • allocated分配的內(nèi)存字節(jié)數(shù)
  • defined_classes線程定義的類(lèi)個(gè)數(shù)
  • tid線程id
  • nidos線程id
  • in Object.wait()表示當(dāng)前線程狀態(tài)
  • [0x00007f10c7d34000]最新java堆棧指針sp
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(java.base@17.0.9/Native Method)
    - waiting on <0x000000071e77a5b0> (a java.lang.Thread)
    at java.lang.Thread.join(java.base@17.0.9/Thread.java:1313)
    - locked <0x000000071e77a5b0> (a java.lang.Thread)
    at java.lang.Thread.join(java.base@17.0.9/Thread.java:1381)
    at org.example.DeadlockDemo.main(DeadlockDemo.java:43)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.9/Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.9/NativeMethodAccessorImpl.java:77)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.9/DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(java.base@17.0.9/Method.java:568)
    at com.sun.tools.javac.launcher.Main.execute(jdk.compiler@17.0.9/Main.java:419)
    at com.sun.tools.javac.launcher.Main.run(jdk.compiler@17.0.9/Main.java:192)
    at com.sun.tools.javac.launcher.Main.main(jdk.compiler@17.0.9/Main.java:132)

后面跟著的是堆棧的詳細(xì)信息,表示當(dāng)前main線程狀態(tài)是WAITING坯辩,表示正在執(zhí)行Thread.join中馁龟,還在等待其他線程執(zhí)行完。Locked ownable synchronizers:代表線程擁有的排它鎖對(duì)象濒翻,例如ReentrantReadWriteLock.writeLock


"VM Thread" os_prio=0 cpu=15.08ms elapsed=91.47s tid=0x00007f10c01724e0 nid=0x2a9b runnable  

"GC Thread#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c007be30 nid=0x2a96 runnable  

"G1 Main Marker" os_prio=0 cpu=0.13ms elapsed=91.48s tid=0x00007f10c008c3e0 nid=0x2a97 runnable  

"G1 Conc#0" os_prio=0 cpu=0.08ms elapsed=91.48s tid=0x00007f10c008d350 nid=0x2a98 runnable  

"G1 Refine#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c0144bc0 nid=0x2a99 runnable  

"G1 Service" os_prio=0 cpu=16.20ms elapsed=91.48s tid=0x00007f10c0145ac0 nid=0x2a9a runnable  

"VM Periodic Task Thread" os_prio=0 cpu=59.59ms elapsed=91.45s tid=0x00007f10c01c6d80 nid=0x2aa6 waiting on condition  

JNI global refs: 9, weak refs: 0

后面跟著的是jvm的線程信息。

Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x00007f1008001120 (object 0x000000071e77a520, a java.lang.Object),
  which is held by "Thread-1"

"Thread-1":
  waiting to lock monitor 0x00007f0ffc000f60 (object 0x000000071e77a510, a java.lang.Object),
  which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-0":
    at org.example.DeadlockDemo.lambda$main$0(DeadlockDemo.java:19)
    - waiting to lock <0x000000071e77a520> (a java.lang.Object)
    - locked <0x000000071e77a510> (a java.lang.Object)
    at org.example.DeadlockDemo$$Lambda$198/0x00007f1044140208.run(Unknown Source)
    at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)
"Thread-1":
    at org.example.DeadlockDemo.lambda$main$1(DeadlockDemo.java:33)
    - waiting to lock <0x000000071e77a510> (a java.lang.Object)
    - locked <0x000000071e77a520> (a java.lang.Object)
    at org.example.DeadlockDemo$$Lambda$199/0x00007f1044140430.run(Unknown Source)
    at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)

Found 1 deadlock.

在堆棧信息的最后有送,jstack已經(jīng)幫我們找到了死鎖的原因淌喻,可以看到,thread-0鎖住了<0x000000071e77a510>對(duì)象雀摘,正在等待<0x000000071e77a520>對(duì)象的釋放裸删,thread-1則相反。

jstack分析死循環(huán)

通過(guò)top找到占用大的java進(jìn)程然后通過(guò)top -Hp pid找到占用異常的線程id21484阵赠,通過(guò)printf "%x\n"轉(zhuǎn)換位16進(jìn)制的pid涯塔,再去jstack輸出找到對(duì)應(yīng)堆棧執(zhí)行位置進(jìn)行分析jstack <pid> | grep -A 20 '<nid>'

引用資料:
[1] JDK14性能管理工具:jstack使用介紹
[2] 原來(lái)jdk自帶了這么好玩的工具 —— 使用 jstack定位死循環(huán)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市清蚀,隨后出現(xiàn)的幾起案子匕荸,更是在濱河造成了極大的恐慌,老刑警劉巖枷邪,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榛搔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡东揣,警方通過(guò)查閱死者的電腦和手機(jī)践惑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嘶卧,“玉大人尔觉,你說(shuō)我怎么就攤上這事〗嬉鳎” “怎么了侦铜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)运沦。 經(jīng)常有香客問(wèn)我泵额,道長(zhǎng),這世上最難降的妖魔是什么携添? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任嫁盲,我火速辦了婚禮,結(jié)果婚禮上烈掠,老公的妹妹穿的比我還像新娘羞秤。我一直安慰自己,他們只是感情好左敌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布瘾蛋。 她就那樣靜靜地躺著,像睡著了一般矫限。 火紅的嫁衣襯著肌膚如雪哺哼。 梳的紋絲不亂的頭發(fā)上佩抹,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音取董,去河邊找鬼棍苹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛茵汰,可吹牛的內(nèi)容都是我干的枢里。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蹂午,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼栏豺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起豆胸,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奥洼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后配乱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體溉卓,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年搬泥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桑寨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忿檩,死狀恐怖尉尾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情燥透,我是刑警寧澤沙咏,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站班套,受9級(jí)特大地震影響肢藐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吱韭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一吆豹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧理盆,春花似錦痘煤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至姨俩,卻和暖如春蘸拔,著一層夾襖步出監(jiān)牢的瞬間师郑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工调窍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呕乎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓陨晶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親帝璧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子先誉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容