jstack 分析一下java程序

感覺(jué)寫的非常不錯(cuò)

前言

如果有一天北滥,你的Java程序長(zhǎng)時(shí)間停頓,也許是它病了恬汁,需要用jstack拍個(gè)片子分析分析伶椿,才能診斷具體什么病癥,是死鎖綜合征氓侧,還是死循環(huán)等其他病癥脊另,本文我們一起來(lái)學(xué)習(xí)jstack命令~

jstack 的功能

jstack用法

線程狀態(tài)等基礎(chǔ)回顧

實(shí)戰(zhàn)案例1:jstack 分析死鎖

實(shí)戰(zhàn)案例2:jstack 分析CPU 過(guò)高

jstack 的功能

jstack是JVM自帶的Java堆棧跟蹤工具,它用于打印出給定的java進(jìn)程ID甘苍、core file尝蠕、遠(yuǎn)程調(diào)試服務(wù)的Java堆棧信息.

jstack prints Java stack traces of Java threads for a given Java process or

core file or a remote debug server.

jstack命令用于生成虛擬機(jī)當(dāng)前時(shí)刻的線程快照。

線程快照是當(dāng)前虛擬機(jī)內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合载庭,生成線程快照的主要目的是定位線程出現(xiàn)長(zhǎng)時(shí)間停頓的原因看彼, 如線程間死鎖、死循環(huán)囚聚、請(qǐng)求外部資源導(dǎo)致的長(zhǎng)時(shí)間等待等問(wèn)題靖榕。

線程出現(xiàn)停頓的時(shí)候通過(guò)jstack來(lái)查看各個(gè)線程的調(diào)用堆棧,就可以知道沒(méi)有響應(yīng)的線程到底在后臺(tái)做什么事情顽铸,或者等待什么資源茁计。

如果java程序崩潰生成core文件,jstack工具可以用來(lái)獲得core文件的java stack和native stack的信息谓松,從而可以輕松地知道java程序是如何崩潰和在程序何處發(fā)生問(wèn)題星压。

另外践剂,jstack工具還可以附屬到正在運(yùn)行的java程序中,看到當(dāng)時(shí)運(yùn)行的java程序的java stack和native stack的信息, 如果現(xiàn)在運(yùn)行的java程序呈現(xiàn)hung的狀態(tài)娜膘,jstack是非常有用的逊脯。

jstack用法

jstack 命令格式如下

jstack [ option ] pid

jstack [ option ] executable core

jstack [ option ] [server-id@]remote-hostname-or-IP

executable Java executable from which the core dump was produced.(可能是產(chǎn)生core dump的java可執(zhí)行程序)

core 將被打印信息的core dump文件

remote-hostname-or-IP 遠(yuǎn)程debug服務(wù)的主機(jī)名或ip

server-id 唯一id,假如一臺(tái)主機(jī)上多個(gè)遠(yuǎn)程debug服務(wù)

最常用的是

jstack [option] <pid>? // 打印某個(gè)進(jìn)程的堆棧信息

option參數(shù)說(shuō)明如下:

選項(xiàng)作用

-F當(dāng)正常輸出的請(qǐng)求不被響應(yīng)時(shí),強(qiáng)制輸出線程堆棧

-m如果調(diào)用到本地方法的話竣贪,可以顯示C/C++的堆棧

-l除堆棧外军洼,顯示關(guān)于鎖的附加信息,在發(fā)生死鎖時(shí)可以用jstack -l pid來(lái)觀察鎖持有情況

線程狀態(tài)等基礎(chǔ)回顧

線程狀態(tài)簡(jiǎn)介

jstack用于生成線程快照的演怎,我們分析線程的情況匕争,需要復(fù)習(xí)一下線程狀態(tài)吧,拿小凳子坐好爷耀,復(fù)習(xí)一下啦~

Java語(yǔ)言定義了6種線程池狀態(tài):

New:創(chuàng)建后尚未啟動(dòng)的線程處于這種狀態(tài)甘桑,不會(huì)出現(xiàn)在Dump中。

RUNNABLE:包括Running和Ready歹叮。線程開(kāi)啟start()方法扇住,會(huì)進(jìn)入該狀態(tài),在虛擬機(jī)內(nèi)執(zhí)行的盗胀。

Waiting:無(wú)限的等待另一個(gè)線程的特定操作艘蹋。

Timed Waiting:有時(shí)限的等待另一個(gè)線程的特定操作。

阻塞(Blocked):在程序等待進(jìn)入同步區(qū)域的時(shí)候票灰,線程將進(jìn)入這種狀態(tài)女阀,在等待監(jiān)視器鎖。

結(jié)束(Terminated):已終止線程的線程狀態(tài)屑迂,線程已經(jīng)結(jié)束執(zhí)行浸策。

Dump文件的線程狀態(tài)一般其實(shí)就以下3種:

RUNNABLE,線程處于執(zhí)行中

BLOCKED惹盼,線程被阻塞

WAITING庸汗,線程正在等待

Monitor 監(jiān)視鎖

因?yàn)镴ava程序一般都是多線程運(yùn)行的,Java多線程跟監(jiān)視鎖環(huán)環(huán)相扣手报,所以我們分析線程狀態(tài)時(shí)蚯舱,也需要回顧一下Monitor監(jiān)視鎖知識(shí)。

有關(guān)于線程同步關(guān)鍵字Synchronized與監(jiān)視鎖的愛(ài)恨情仇掩蛤,有興趣的伙伴可以看一下我這篇文章Synchronized解析——如果你愿意一層一層剝開(kāi)我的心

Monitor的工作原理圖如下:

線程想要獲取monitor,首先會(huì)進(jìn)入Entry Set隊(duì)列枉昏,它是Waiting Thread,線程狀態(tài)是Waiting for monitor entry揍鸟。

當(dāng)某個(gè)線程成功獲取對(duì)象的monitor后,進(jìn)入Owner區(qū)域兄裂,它就是Active Thread。

如果線程調(diào)用了wait()方法,則會(huì)進(jìn)入Wait Set隊(duì)列晰奖,它會(huì)釋放monitor鎖谈撒,它也是Waiting Thread,線程狀態(tài)in Object.wait()

如果其他線程調(diào)用 notify() / notifyAll() 匾南,會(huì)喚醒Wait Set中的某個(gè)線程港华,該線程再次嘗試獲取monitor鎖,成功即進(jìn)入Owner區(qū)域午衰。

Dump 文件分析關(guān)注重點(diǎn)

runnable,線程處于執(zhí)行中

deadlock冒萄,死鎖(重點(diǎn)關(guān)注)

blocked臊岸,線程被阻塞 (重點(diǎn)關(guān)注)

Parked,停止

locked尊流,對(duì)象加鎖

waiting帅戒,線程正在等待

waiting to lock 等待上鎖

Object.wait(),對(duì)象等待中

waiting for monitor entry 等待獲取監(jiān)視器(重點(diǎn)關(guān)注)

Waiting on condition崖技,等待資源(重點(diǎn)關(guān)注)逻住,最常見(jiàn)的情況是線程在等待網(wǎng)絡(luò)的讀寫

實(shí)戰(zhàn)案例1:jstack 分析死鎖問(wèn)題

什么是死鎖?

如何用jstack排查死鎖迎献?

什么是死鎖瞎访?

死鎖是指兩個(gè)或兩個(gè)以上的線程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象吁恍,若無(wú)外力作用扒秸,它們都將無(wú)法進(jìn)行下去。

如何用如何用jstack排查死鎖問(wèn)題

先來(lái)看一段會(huì)產(chǎn)生死鎖的Java程序冀瓦,源碼如下:

/**

* Java 死鎖demo

*/

public class DeathLockTest {

? ? private static Lock lock1 = new ReentrantLock();

? ? private static Lock lock2 = new ReentrantLock();

? ? public static void deathLock() {

? ? ? ? Thread t1 = new Thread() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? lock1.lock();

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " get the lock1");

? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);

? ? ? ? ? ? ? ? ? ? lock2.lock();

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " get the lock2");

? ? ? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? Thread t2 = new Thread() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? lock2.lock();

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " get the lock2");

? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);

? ? ? ? ? ? ? ? ? ? lock1.lock();

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " get the lock1");

? ? ? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? //設(shè)置線程名字伴奥,方便分析堆棧信息

? ? ? ? t1.setName("mythread-jay");

? ? ? ? t2.setName("mythread-tianluo");

? ? ? ? t1.start();

? ? ? ? t2.start();

? ? }

? ? public static void main(String[] args) {

? ? ? ? deathLock();

? ? }

}

運(yùn)行結(jié)果:

顯然,線程jay和線程tianluo都是只執(zhí)行到一半翼闽,就陷入了阻塞等待狀態(tài)~

jstack排查Java死鎖步驟

在終端中輸入jsp查看當(dāng)前運(yùn)行的java程序

使用 jstack -l pid 查看線程堆棧信息

分析堆棧信息

在終端中輸入jsp查看當(dāng)前運(yùn)行的java程序

通過(guò)使用 jps 命令獲取需要監(jiān)控的進(jìn)程的pid拾徙,我們找到了23780DeathLockTest

使用 jstack -l pid 查看線程堆棧信息

由上圖,可以清晰看到死鎖信息:

mythread-tianluo 等待這個(gè)鎖 “0x00000000d61ae3a0”感局,這個(gè)鎖是由mythread-jay線程持有尼啡。

mythread-jay線程等待這個(gè)鎖“0x00000000d61ae3d0”,這個(gè)鎖是由mythread-tianluo 線程持有。

還原死鎖真相

“mythread-tianluo"線程堆棧信息分析如下:

mythread-tianluo的線程處于等待(waiting)狀態(tài)询微,持有“0x00000000d61ae3d0”鎖玄叠,等待“0x00000000d61ae3a0”的鎖

“mythread-jay"線程堆棧信息分析如下:

mythread-tianluo的線程處于等待(waiting)狀態(tài),持有“0x00000000d61ae3a0”鎖拓提,等待“0x00000000d61ae3d0”的鎖

實(shí)戰(zhàn)案例2:jstack 分析CPU過(guò)高問(wèn)題

來(lái)個(gè)導(dǎo)致CPU過(guò)高的demo程序读恃,一個(gè)死循環(huán),哈哈~

/**

* 有個(gè)導(dǎo)致CPU過(guò)高程序的demo,死循環(huán)

*/

public class JstackCase {

? ? private static ExecutorService executorService = Executors.newFixedThreadPool(5);

? ? public static void main(String[] args) {

? ? ? ? Task task1 = new Task();

? ? ? ? Task task2 = new Task();

? ? ? ? executorService.execute(task1);

? ? ? ? executorService.execute(task2);

? ? }

? ? public static Object lock = new Object();

? ? static class Task implements Runnable{

? ? ? ? public void run() {

? ? ? ? ? ? synchronized (lock){

? ? ? ? ? ? ? ? long sum = 0L;

? ? ? ? ? ? ? ? while (true){

? ? ? ? ? ? ? ? ? ? sum += 1;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

jstack 分析CPU過(guò)高步驟

top

? ? ?2.top -Hp pid

? ? ?3. jstack pid

? ? ?4. jstack -l [PID] >/tmp/log.txt

? ? ?5. 分析堆棧信息

1.top

在服務(wù)器上寺惫,我們可以通過(guò)top命令查看各個(gè)進(jìn)程的cpu使用情況疹吃,它默認(rèn)是按cpu使用率由高到低排序的

由上圖中,我們可以找出pid為21340的java進(jìn)程西雀,它占用了最高的cpu資源萨驶,兇手就是它,哈哈艇肴!

2. top -Hp pid

通過(guò)top -Hp 21340可以查看該進(jìn)程下腔呜,各個(gè)線程的cpu使用情況,如下:

可以發(fā)現(xiàn)pid為21350的線程再悼,CPU資源占用最高~核畴,嘻嘻,小本本把它記下來(lái)冲九,接下來(lái)拿jstack給它拍片子~

3. jstack pid

通過(guò)top命令定位到cpu占用率較高的線程之后谤草,接著使用jstack pid命令來(lái)查看當(dāng)前java進(jìn)程的堆棧狀態(tài),jstack21350后莺奸,內(nèi)容如下:

4. jstack -l [PID] >/tmp/log.txt

其實(shí)丑孩,前3個(gè)步驟,堆棧信息已經(jīng)出來(lái)啦灭贷。但是一般在生成環(huán)境温学,我們可以把這些堆棧信息打到一個(gè)文件里,再回頭仔細(xì)分析哦~

5. 分析堆棧信息

我們把占用cpu資源較高的線程pid(本例子是21350)甚疟,將該pid轉(zhuǎn)成16進(jìn)制的值

在thread dump中枫浙,每個(gè)線程都有一個(gè)nid,我們找到對(duì)應(yīng)的nid(5366)古拴,發(fā)現(xiàn)一直在跑(24行)

這個(gè)時(shí)候箩帚,可以去檢查代碼是否有問(wèn)題啦~ 當(dāng)然,也建議隔段時(shí)間再執(zhí)行一次stack命令黄痪,再一份獲取thread dump紧帕,畢竟兩次拍片結(jié)果(jstack)對(duì)比,更準(zhǔn)確嘛~

參考與感謝

jvm 性能調(diào)優(yōu)工具之 jstack (http://www.reibang.com/p/025cb069cb69)

如何使用jstack分析線程狀態(tài) (http://www.reibang.com/p/6690f7e92f27)

Java命令學(xué)習(xí)系列(二)—Jstack(http://www.hollischuang.com/archives/110)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桅打,一起剝皮案震驚了整個(gè)濱河市是嗜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挺尾,老刑警劉巖鹅搪,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異遭铺,居然都是意外死亡丽柿,警方通過(guò)查閱死者的電腦和手機(jī)恢准,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)甫题,“玉大人馁筐,你說(shuō)我怎么就攤上這事∽狗牵” “怎么了敏沉?”我有些...
    開(kāi)封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)炎码。 經(jīng)常有香客問(wèn)我盟迟,道長(zhǎng),這世上最難降的妖魔是什么潦闲? 我笑而不...
    開(kāi)封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任攒菠,我火速辦了婚禮,結(jié)果婚禮上矫钓,老公的妹妹穿的比我還像新娘。我一直安慰自己舍杜,他們只是感情好新娜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著既绩,像睡著了一般概龄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饲握,一...
    開(kāi)封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天私杜,我揣著相機(jī)與錄音,去河邊找鬼救欧。 笑死衰粹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笆怠。 我是一名探鬼主播铝耻,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蹬刷!你這毒婦竟也來(lái)了瓢捉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤办成,失蹤者是張志新(化名)和其女友劉穎泡态,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體迂卢,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡某弦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年桐汤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刀崖。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惊科,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亮钦,到底是詐尸還是另有隱情馆截,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布蜂莉,位于F島的核電站蜡娶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏映穗。R本人自食惡果不足惜窖张,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚁滋。 院中可真熱鬧宿接,春花似錦、人聲如沸辕录。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)走诞。三九已至副女,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚣旱,已是汗流浹背碑幅。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塞绿,地道東北人沟涨。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像异吻,于是被迫代替她去往敵國(guó)和親拷窜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353