jstack命令主要用于調(diào)試java程序運行過程中的線程堆棧信息蠕啄,可以用于檢測死鎖,進程耗用cpu過高報警問題的排查馅巷。
jstack語法格式:
[root@www wangxiaoxiao]# jstack
Usage:
jstack [-l] <pid>
jstack -F [-m] [-l] <pid>
Options:
-F 強制dump線程堆棧信息. 用于進程hung住急前, jstack <pid>命令沒有響應(yīng)的情況
-m 同時打印java和本地(native)線程棧信息狈网,m是mixed mode的簡寫
-l 打印鎖的額外信息
jstack命令會打印出所有的線程礼搁,包括用戶自己啟動的線程和jvm后臺線程,我們主要關(guān)注的是用戶線程目尖,如
[root@www wangxiaoxiao]# jstack 15525
2017-02-14 21:10:02
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.65-b01 mixed mode):
**"elasticsearch[Native][merge][T#1]" **#98** daemon prio=5 os_prio=0 tid=0x00007f031c009000 nid=0x4129 waiting on condition [0x00007f02f61ee000]**
**java.lang.Thread.State: WAITING (parking)**
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000eea589f0> (a org.elasticsearch.common.util.concurrent.EsExecutors$ExecutorScalingQueue)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.LinkedTransferQueue.awaitMatch(LinkedTransferQueue.java:737)
at java.util.concurrent.LinkedTransferQueue.xfer(LinkedTransferQueue.java:647)
at java.util.concurrent.LinkedTransferQueue.take(LinkedTransferQueue.java:1269)
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)
....
線程dump信息說明:
elasticsearch[Native][merge][T#1] 是我們?yōu)榫€程起的名字
**daemon **表示線程是否是守護線程
**prio **表示我們?yōu)榫€程設(shè)置的優(yōu)先級
**os_prio **表示的對應(yīng)的操作系統(tǒng)線程的優(yōu)先級馒吴,由于并不是所有的操作系統(tǒng)都支持線程優(yōu)先級,所以可能會出現(xiàn)都置為0的情況
**tid **是java中為這個線程的id
**nid **是這個線程對應(yīng)的操作系統(tǒng)本地線程id瑟曲,每一個java線程都有一個對應(yīng)的操作系統(tǒng)線程
wait on condition表示當(dāng)前線程處于等待狀態(tài)饮戳,但是并沒列出具體原因
java.lang.Thread.State: WAITING (parking) 也是表示的處于等待狀態(tài),括號中的內(nèi)容說明了導(dǎo)致等待的原因洞拨,例如這里的parking說明是因為調(diào)用了 LockSupport.park方法導(dǎo)致等待
java.lang.Thread.State說明:
一個Thread對象可以有多個狀態(tài)扯罐,在java.lang.Thread.State
中,總共定義六種狀態(tài):
1烦衣、NEW
線程剛剛被創(chuàng)建歹河,也就是已經(jīng)new過了,但是還沒有調(diào)用start()方法花吟,jstack命令不會列出處于此狀態(tài)的線程信息
2秸歧、RUNNABLE #java.lang.Thread.State: RUNNABLE
RUNNABLE這個名字很具有欺騙性,很容易讓人誤以為處于這個狀態(tài)的線程正在運行衅澈。事實上键菱,這個狀態(tài)只是表示,線程是可運行的今布。我們已經(jīng)無數(shù)次提到過经备,一個單核CPU在同一時刻拭抬,只能運行一個線程。
3侵蒙、BLOCKED # java.lang.Thread.State: BLOCKED (on object monitor)
線程處于阻塞狀態(tài)造虎,正在等待一個monitor lock。通常情況下蘑志,是因為本線程與其他線程公用了一個鎖累奈。其他在線程正在使用這個鎖進入某個synchronized同步方法塊或者方法,而本線程進入這個同步代碼塊也需要這個鎖急但,最終導(dǎo)致本線程處于阻塞狀態(tài)澎媒。
4、WAITING
等待狀態(tài)波桩,調(diào)用以下方法可能會導(dǎo)致一個線程處于等待狀態(tài):
-
Object.wait
不指定超時時間# java.lang.Thread.State: WAITING (on object monitor)
-
[Thread.join]
with no timeout [LockSupport.park]#java.lang.Thread.State: WAITING (parking)
例如:對于wait()方法戒努,一個線程處于等待狀態(tài),通常是在等待其他線程完成某個操作镐躲。本線程調(diào)用某個對象的wait()方法储玫,其他線程處于完成之后,調(diào)用同一個對象的notify或者notifyAll()方法萤皂。Object.wait()方法只能夠在同步代碼塊中調(diào)用撒穷。調(diào)用了wait()方法后,會釋放鎖裆熙。
5端礼、TIMED_WAITING
線程等待指定的時間,對于以下方法的調(diào)用入录,可能會導(dǎo)致線程處于這個狀態(tài):
[Thread.sleep] #java.lang.Thread.State: TIMED_WAITING(sleeping)
-
[Object.wait]
指定超時時間 #java.lang.Thread.State: TIMED_WAITING (on object monitor) -
[Thread.join]
with timeout [LockSupport.parkNanos] #java.lang.Thread.State: TIMED_WAITING (parking)
[LockSupport.parkUntil] #java.lang.Thread.State: TIMED_WAITING (parking)
6蛤奥、TERMINATED
線程終止。
說明僚稿,對于** java.lang.Thread.State: WAITING (on object monitor)和java.lang.Thread.State: TIMED_WAITING (on object monitor)**凡桥,對于這兩個狀態(tài),是因為調(diào)用了Object的wait方法(前者沒有指定超時蚀同,后者指定了超時)缅刽,由于wait方法肯定要在syncronized代碼中編寫,因此肯定是如類似以下代碼導(dǎo)致:
synchronized(obj) {
.........
obj.wait();
.........
}
因此通常的堆棧信息中蠢络,必然后一個lock標(biāo)記拷恨,如下:
"CM1" #21 daemon prio=5 os_prio=0 tid=0x00007f02f0d6d800 nid=0x3d48 in Object.wait() [0x00007f02fefef000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.util.TimerThread.mainLoop(Timer.java:526)
** - locked <0x00000000eca75aa8> (a java.util.TaskQueue)**
at java.util.TimerThread.run(Timer.java:505)
關(guān)于死鎖
在 JAVA 5中加強了對死鎖的檢測。線程 Dump中可以直接報告出 Java級別的死鎖谢肾,如下所示:
**Found one Java-level deadlock:**
=============================
"Thread-1":
waiting to lock monitor 0x0003f334 (object 0x22c19f18, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0003f314 (object 0x22c19f20, a java.lang.Object),
which is held by "Thread-1"
關(guān)于nid
每個線程都有一個tid 和nid腕侄,tid是java中這個線程的編號,而nid(native id)是對應(yīng)操作系統(tǒng)線程id。有的時候冕杠,我們會收到報警微姊,說服務(wù)器,某個進程占用CPU過高分预,肯定是因為某個java線程有耗CPU資源的方法兢交。
可以通過命令cat /proc/<pid>/task
查看一個進程下面有哪些線程
可以通過"top -Hp
"命令查看這個進程內(nèi)所有線程的cpu和內(nèi)容使用情況使用情況:
上例中看到12446進程中,12467線程占用cpu最高笼痹,是0.3%配喳。要想看這個線程對應(yīng)java哪個線程,首先用以下命令將12467轉(zhuǎn)成16進制:
接著使用jstack命令凳干,顯示堆棧信息: