天天看點

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

介紹

jstack是java虛拟機自帶的一種堆棧跟蹤工具。jstack用于列印出給定的java程序ID或core file或遠端調試服務的Java堆棧資訊,如果是在64位機器上,需要指定選項"-J-d64",Windows的jstack使用方式隻支援以下的這種方式:

jstack [-l] pid

主要分為兩個功能:

a.  針對活着的程序做本地的或遠端的線程dump;

b.  針對core檔案做線程dump。

jstack用于生成java虛拟機目前時刻的線程快照。線程快照是目前java虛拟機内每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導緻的長時間等待等。 線程出現停頓的時候通過jstack來檢視各個線程的調用堆棧,就可以知道沒有響應的線程到底在背景做什麼事情,或者等待什麼資源。 如果java程式崩潰生成core檔案,jstack工具可以用來獲得core檔案的java stack和native stack的資訊,進而可以輕松地知道java程式是如何崩潰和在程式何處發生問題。另外,jstack工具還可以附屬到正在運作的java程式中,看到當時運作的java程式的java stack和native stack的資訊, 如果現在運作的java程式呈現hung的狀态,jstack是非常有用的。

So,jstack指令主要用來檢視Java線程的調用堆棧的,可以用來分析線程問題(如死鎖)。

線程狀态

想要通過jstack指令來分析線程的情況的話,首先要知道線程都有哪些狀态,下面這些狀态是我們使用jstack指令檢視線程堆棧資訊時可能會看到的線程的幾種狀态:

NEW,未啟動的。不會出現在Dump中。

RUNNABLE,在虛拟機内執行的。運作中狀态,可能裡面還能看到locked字樣,表明它獲得了某把鎖。

BLOCKED,受阻塞并等待螢幕鎖。被某個鎖(synchronizers)給block住了。

WATING,無限期等待另一個線程執行特定操作。等待某個condition或monitor發生,一般停留在park(), wait(), sleep(),join() 等語句裡。

TIMED_WATING,有時限的等待另一個線程的特定操作。和WAITING的差別是wait() 等語句加上了時間限制 wait(timeout)。

TERMINATED,已退出的。

Monitor

在多線程的 JAVA程式中,實作線程之間的同步,就要說說 Monitor。 Monitor是 Java中用以實作線程之間的互斥與協作的主要手段,它可以看成是對象或者 Class的鎖。每一個對象都有,也僅有一個 monitor。下 面這個圖,描述了線程和 Monitor之間關系,以 及線程的狀态轉換圖:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

進入區(Entrt Set):表示線程通過synchronized要求擷取對象的鎖。如果對象未被鎖住,則迚入擁有者;否則則在進入區等待。一旦對象鎖被其他線程釋放,立即參與競争。

擁有者(The Owner):表示某一線程成功競争到對象鎖。

等待區(Wait Set):表示線程通過對象的wait方法,釋放對象的鎖,并在等待區等待被喚醒。

從圖中可以看出,一個 Monitor在某個時刻,隻能被一個線程擁有,該線程就是 “Active Thread”,而其它線程都是 “Waiting Thread”,分别在兩個隊列 “ Entry Set”和 “Wait Set”裡面等候。在 “Entry Set”中等待的線程狀态是 “Waiting for monitor entry”,而在“Wait Set”中等待的線程狀态是 “in Object.wait()”。 先看 “Entry Set”裡面的線程。我們稱被 synchronized保護起來的代碼段為臨界區。當一個線程申請進入臨界區時,它就進入了 “Entry Set”隊列。對應的 code就像:

synchronized(obj) {

.........

}

調用修飾

表示線程在方法調用時,額外的重要的操作。線程Dump分析的重要資訊。修飾上方的方法調用。

locked 目标:使用synchronized申請對象鎖成功,螢幕的擁有者。

waiting to lock 目标:使用synchronized申請對象鎖未成功,在迚入區等待。

waiting on 目标:使用synchronized申請對象鎖成功後,釋放鎖幵在等待區等待。

parking to wait for 目标

locked

at oracle.jdbc.driver.PhysicalConnection.prepareStatement

- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)

at oracle.jdbc.driver.PhysicalConnection.prepareStatement

- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)

at com.jiuqi.dna.core.internal.db.datasource.PooledConnection.prepareStatement

通過synchronized關鍵字,成功擷取到了對象的鎖,成為螢幕的擁有者,在臨界區内操作。對象鎖是可以線程重入的。

waiting to lock

at com.jiuqi.dna.core.impl.CacheHolder.isVisibleIn(CacheHolder.java:165)

- waiting to lock <0x0000000097ba9aa8> (a CacheHolder)

at com.jiuqi.dna.core.impl.CacheGroup$Index.findHolder

at com.jiuqi.dna.core.impl.ContextImpl.find

at com.jiuqi.dna.bap.basedata.common.util.BaseDataCenter.findInfo

通過synchronized關鍵字,沒有擷取到了對象的鎖,線程在螢幕的進入區等待。在調用棧頂出現,線程狀态為Blocked。

waiting on

at java.lang.Object.wait(Native Method)

- waiting on <0x00000000da2defb0> (a WorkingThread)

at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo

- locked <0x00000000da2defb0> (a WorkingThread)

at com.jiuqi.dna.core.impl.WorkingThread.run

通過synchronized關鍵字,成功擷取到了對象的鎖後,調用了wait方法,進入對象的等待區等待。在調用棧頂出現,線程狀态為WAITING或TIMED_WATING。

parking to wait for

park是基本的線程阻塞原語,不通過螢幕在對象上阻塞。随concurrent包會出現的新的機制,不synchronized體系不同。

線程動作

線程狀态産生的原因

runnable:狀态一般為RUNNABLE。

in Object.wait():等待區等待,狀态為WAITING或TIMED_WAITING。

waiting for monitor entry:進入區等待,狀态為BLOCKED。

waiting on condition:等待區等待、被park。

sleeping:休眠的線程,調用了Thread.sleep()。

Wait on condition 該狀态出現線上程等待某個條件的發生。具體是什麼原因,可以結合 stacktrace來分析。 最常見的情況就是線程處于sleep狀态,等待被喚醒。 常見的情況還有等待網絡IO:在java引入nio之前,對于每個網絡連接配接,都有一個對應的線程來處理網絡的讀寫操作,即使沒有可讀寫的資料,線程仍然阻塞在讀寫操作上,這樣有可能造成資源浪費,而且給作業系統的線程排程也帶來壓力。在 NewIO裡采用了新的機制,編寫的伺服器程式的性能和可擴充性都得到提高。 正等待網絡讀寫,這可能是一個網絡瓶頸的征兆。因為網絡阻塞導緻線程無法執行。一種情況是網絡非常忙,幾 乎消耗了所有的帶寬,仍然有大量資料等待網絡讀 寫;另一種情況也可能是網絡空閑,但由于路由等問題,導緻包無法正常的到達。是以要結合系統的一些性能觀察工具來綜合分析,比如 netstat統計機關時間的發送包的數目,如果很明顯超過了所在網絡帶寬的限制 ; 觀察 cpu的使用率,如果系統态的 CPU時間,相對于使用者态的 CPU時間比例較高;如果程式運作在 Solaris 10平台上,可以用 dtrace工具看系統調用的情況,如果觀察到 read/write的系統調用的次數或者運作時間遙遙領先;這些都指向由于網絡帶寬所限導緻的網絡瓶頸。(來自http://www.blogjava.net/jzone/articles/303979.html)

指令格式

jstack [ option ] pid

jstack [ option ] executable core

jstack [ option ] [[email protected]]remote-hostname-or-IP

常用參數說明

1)options:

executable Java executable from which the core dump was produced.(可能是産生core dump的java可執行程式)

core 将被列印資訊的core dump檔案

remote-hostname-or-IP 遠端debug服務的主機名或ip

server-id 唯一id,假如一台主機上多個遠端debug服務

2)基本參數:

-F當’jstack [-l] pid’沒有相應的時候強制列印棧資訊,如果直接jstack無響應時,用于強制jstack),一般情況不需要使用

-l長清單. 列印關于鎖的附加資訊,例如屬于java.util.concurrent的ownable synchronizers清單,會使得JVM停頓得長久得多(可能會差很多倍,比如普通的jstack可能幾毫秒和一次GC沒差別,加了-l 就是近一秒的時間),-l 建議不要用。一般情況不需要使用

-m列印java和native c/c++架構的所有棧資訊.可以列印JVM的堆棧,顯示上Native的棧幀,一般應用排查不需要使用

-h | -help列印幫助資訊

pid 需要被列印配置資訊的java程序id,可以用jps查詢.

線程dump的分析工具:

使用執行個體

1、jstack pid

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

~$ jps -ml

org.apache.catalina.startup.Bootstrap

~$ jstack 5661

2013-04-16 21:09:27

Full thread dump Java HotSpot(TM) Server VM (20.10-b01 mixed mode):

"Attach Listener" daemon prio=10 tid=0x70e95400 nid=0x2265 waiting on condition [0x00000000]

java.lang.Thread.State: RUNNABLE

"http-bio-8080-exec-20" daemon prio=10 tid=0x08a35800 nid=0x1d42 waiting on condition [0x70997000]

java.lang.Thread.State: WAITING (parking)

at sun.misc.Unsafe.park(Native Method)

- parking to wait for <0x766a27b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)

at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)

at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:399)

at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)

at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)

at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)

at java.lang.Thread.run(Thread.java:662)

........

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

#jstack -l 4089 >1.txt,檢視1.txt内容如下所示:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

2014-03-14 10:47:04

Full thread dump Java HotSpot(TM) Client VM (20.45-b01 mixed mode, sharing):

"Attach Listener" daemon prio=10 tid=0x08251400 nid=0x11bd runnable [0x00000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"DestroyJavaVM" prio=10 tid=0xb3a0a800 nid=0xffa waiting on condition [0x00000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"Query Listener" prio=10 tid=0xb3a09800 nid=0x1023 runnable [0xb3b72000]

java.lang.Thread.State: RUNNABLE

at java.net.PlainSocketImpl.socketAccept(Native Method)

at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)

- locked <0x70a84430> (a java.net.SocksSocketImpl)

at java.net.ServerSocket.implAccept(ServerSocket.java:462)

at java.net.ServerSocket.accept(ServerSocket.java:430)

at com.sun.tools.hat.internal.server.QueryListener.waitForRequests(QueryListener.java:76)

at com.sun.tools.hat.internal.server.QueryListener.run(QueryListener.java:65)

at java.lang.Thread.run(Thread.java:662)

Locked ownable synchronizers:

- None

"Low Memory Detector" daemon prio=10 tid=0x08220400 nid=0x1000 runnable [0x00000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"C1 CompilerThread0" daemon prio=10 tid=0x08214c00 nid=0xfff waiting on condition [0x00000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"Signal Dispatcher" daemon prio=10 tid=0x08213000 nid=0xffe runnable [0x00000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"Finalizer" daemon prio=10 tid=0x0820bc00 nid=0xffd in Object.wait() [0xb5075000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x7a2b6f50> (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)

- locked <0x7a2b6f50> (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)

at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:171)

Locked ownable synchronizers:

- None

"Reference Handler" daemon prio=10 tid=0x0820a400 nid=0xffc in Object.wait() [0xb50c7000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x7a2b6fe0> (a java.lang.ref.Reference$Lock)

at java.lang.Object.wait(Object.java:485)

at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)

- locked <0x7a2b6fe0> (a java.lang.ref.Reference$Lock)

Locked ownable synchronizers:

- None

"VM Thread" prio=10 tid=0x08200000 nid=0xffb runnable

"VM Periodic Task Thread" prio=10 tid=0x08222400 nid=0x1001 waiting on condition

JNI global references: 1317

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

一般情況下,通過jstack輸出的線程資訊主要包括:jvm自身線程、使用者線程等。其中jvm線程會在jvm啟動時就會存在。對于使用者線程則是在使用者通路時才會生成。

2、jstack 檢視線程具體在做什麼,可看出哪些線程在長時間占用CPU,盡快定位問題和解決問題

http://www.iteye.com/topic/1114219

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

1.top查找出哪個程序消耗的cpu高。執行top指令,預設是程序視圖,其中PID是程序号

21125 co_ad2    18   0 1817m 776m 9712 S  3.3  4.9  12:03.24 java

5284 co_ad     21   0 3028m 2.5g 9432 S  1.0 16.3   6629:44 ja

這裡我們分析21125這個java程序

2.top中shift+h 或“H”查找出哪個線程消耗的cpu高

先輸入top,然後再按shift+h 或“H”,此時打開的是線程視圖,pid為線程号

21233 co_ad2 15 0 1807m 630m 9492 S 1.3 4.0 0:05.12 java

20503 co_ad2_s 15 0 1360m 560m 9176 S 0.3 3.6 0:46.72 java

這裡我們分析21233這個線程,并且注意的是,這個線程是屬于21125這個程序的。

3.使用jstack指令輸出這一時刻的線程棧,儲存到檔案,命名為jstack.log。注意:輸出線程棧和儲存top指令快照盡量同時進行。

由于jstack.log檔案記錄的線程ID是16進制,需要将top指令展示的線程号轉換為16進制。

4. jstack查找這個線程的資訊

jstack [程序]|grep -A 10 [線程的16進制]

即: jstack 21125|grep -A 10 52f1

-A 10表示查找到所在行的後10行。21233用電腦轉換為16進制52f1,注意字母是小寫。

結果:

"http-8081-11" daemon prio=10 tid=0x00002aab049a1800 nid=0x52bb in Object.wait() [0x0000000042c75000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at java.lang.Object.wait(Object.java:485)

at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)

在結果中查找52f1,可看到目前線程在做什麼。

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

3、代碼示例

運作代碼:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

public class JStackDemo1 {

public static void main(String[] args) {

while (true) {

//Do Nothing

}

}

}

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

先是有jps檢視程序号:

[email protected]:~$ jps

29788 JStackDemo1

29834 Jps

22385 org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar

然後使用jstack 檢視堆棧資訊:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

[email protected]:~$ jstack 29788

2015-04-17 23:47:31

...此處省略若幹内容...

"main" prio=10 tid=0x00007f197800a000 nid=0x7462 runnable [0x00007f197f7e1000]

java.lang.Thread.State: RUNNABLE

at javaCommand.JStackDemo1.main(JStackDemo1.java:7)

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

我們可以從這段堆棧資訊中看出什麼來呢?我們可以看到,目前一共有一條使用者級别線程,線程處于runnable狀态,執行到JStackDemo1.java的第七行。 看下面代碼:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

public class JStackDemo1 {

public static void main(String[] args) {

Thread thread = new Thread(new Thread1());

thread.start();

}

}

class Thread1 implements Runnable{

@Override

public void run() {

while(true){

System.out.println(1);

}

}

}

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

線程堆棧資訊如下:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

"Reference Handler" daemon prio=10 tid=0x00007fbbcc06e000 nid=0x286c in Object.wait() [0x00007fbbc8dfc000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x0000000783e066e0> (a java.lang.ref.Reference$Lock)

at java.lang.Object.wait(Object.java:503)

at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)

- locked <0x0000000783e066e0> (a java.lang.ref.Reference$Lock)

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

我們能看到:

線程的狀态: WAITING 線程的調用棧 線程的目前鎖住的資源: <0x0000000783e066e0> 線程目前等待的資源:<0x0000000783e066e0>

為什麼同時鎖住的等待同一個資源:

線程的執行中,先獲得了這個對象的 Monitor(對應于 locked <0x0000000783e066e0>)。當執行到 obj.wait(), 線程即放棄了 Monitor的所有權,進入 “wait set”隊列(對應于 waiting on <0x0000000783e066e0> )。

如何分析

1、線程Dump的分析

原則

結合代碼閱讀的推理。需要線程Dump和源碼的互相推導和印證。

造成Bug的根源往往丌會在調用棧上直接展現,一定格外注意線程目前調用之前的所有調用。

入手點

進入區等待

"d&a-3588" daemon waiting for monitor entry [0x000000006e5d5000]

java.lang.Thread.State: BLOCKED (on object monitor)

at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()

- waiting to lock <0x0000000602f38e90> (a java.lang.Object)

at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()

線程狀态BLOCKED,線程動作wait on monitor entry,調用修飾waiting to lock總是一起出現。表示在代碼級别已經存在沖突的調用。必然有問題的代碼,需要盡可能減少其發生。

同步塊阻塞

一個線程鎖住某對象,大量其他線程在該對象上等待。

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

"blocker" runnable

java.lang.Thread.State: RUNNABLE

at com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)

- locked <0x00000000eb8eff68> (a java.lang.Object)

"blockee-11" waiting for monitor entry

java.lang.Thread.State: BLOCKED (on object monitor)

at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)

- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)

"blockee-86" waiting for monitor entry

java.lang.Thread.State: BLOCKED (on object monitor)

at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)

- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

持續運作的IO IO操作是可以以RUNNABLE狀态達成阻塞。例如:資料庫死鎖、網絡讀寫。 格外注意對IO線程的真實狀态的分析。 一般來說,被捕捉到RUNNABLE的IO調用,都是有問題的。

以下堆棧顯示: 線程狀态為RUNNABLE。 調用棧在SocketInputStream或SocketImpl上,socketRead0等方法。 調用棧包含了jdbc相關的包。很可能發生了資料庫死鎖

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

"d&a-614" daemon prio=6 tid=0x0000000022f1f000 nid=0x37c8 runnable

[0x0000000027cbd000]

java.lang.Thread.State: RUNNABLE

at java.net.SocketInputStream.socketRead0(Native Method)

at java.net.SocketInputStream.read(Unknown Source)

at oracle.net.ns.Packet.receive(Packet.java:240)

at oracle.net.ns.DataPacket.receive(DataPacket.java:92)

at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:172)

at oracle.net.ns.NetInputStream.read(NetInputStream.java:117)

at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1034)

at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:588)

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

分線程排程的休眠

正常的線程池等待

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

"d&a-131" in Object.wait()

java.lang.Thread.State: TIMED_WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo(WorkingManager.java:322)

- locked <0x0000000313f656f8> (a com.jiuqi.dna.core.impl.WorkingThread)

at com.jiuqi.dna.core.impl.WorkingThread.run(WorkingThread.java:40)

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

可疑的線程等待

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

"d&a-121" in Object.wait()

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at java.lang.Object.wait(Object.java:485)

at com.jiuqi.dna.core.impl.AcquirableAccessor.exclusive()

- locked <0x00000003011678d8> (a com.jiuqi.dna.core.impl.CacheGroup)

at com.jiuqi.dna.core.impl.Transaction.lock()

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

入手點總結

wait on monitor entry: 被阻塞的,肯定有問題

runnable : 注意IO線程

in Object.wait(): 注意非線程池等待

2、死鎖分析

學會了怎麼使用jstack指令之後,我們就可以看看,如何使用jstack分析死鎖了,這也是我們一定要掌握的内容。 啥叫死鎖? 所謂死鎖: 是指兩個或兩個以上的程序在執行過程中,由于競争資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都将無法推進下去。此時稱系統處于死鎖狀态或系統産生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。 說白了,我現在想吃雞蛋灌餅,桌子上放着雞蛋和餅,但是我和我的朋友同時分别拿起了雞蛋和病,我手裡拿着雞蛋,但是我需要他手裡的餅。他手裡拿着餅,但是他想要我手裡的雞蛋。就這樣,如果不能同時拿到雞蛋和餅,那我們就不能繼續做後面的工作(做雞蛋灌餅)。是以,這就造成了死鎖。 看一段死鎖的程式:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

package javaCommand;

public class JStackDemo {

public static void main(String[] args) {

Thread t1 = new Thread(new DeadLockclass(true));//建立一個線程

Thread t2 = new Thread(new DeadLockclass(false));//建立另一個線程

t1.start();//啟動一個線程

t2.start();//啟動另一個線程

}

}

class DeadLockclass implements Runnable {

public boolean falg;// 控制線程

DeadLockclass(boolean falg) {

this.falg = falg;

}

public void run() {

if (falg) {

while (true) {

synchronized (Suo.o1) {

System.out.println("o1 " + Thread.currentThread().getName());

synchronized (Suo.o2) {

System.out.println("o2 " + Thread.currentThread().getName());

}

}

}

}

else {

while (true) {

synchronized (Suo.o2) {

System.out.println("o2 " + Thread.currentThread().getName());

synchronized (Suo.o1) {

System.out.println("o1 " + Thread.currentThread().getName());

}

}

}

}

}

}

class Suo {

static Object o1 = new Object();

static Object o2 = new Object();

}

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

當我啟動該程式時,我們看一下控制台:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

我們發現,程式隻輸出了兩行内容,然後程式就不再列印其它的東西了,但是程式并沒有停止。這樣就産生了死鎖。 當線程1使用synchronized鎖住了o1的同時,線程2也是用synchronized鎖住了o2。當兩個線程都執行完第一個列印任務的時候,線程1想鎖住o2,線程2想鎖住o1。但是,線程1目前鎖着o1,線程2鎖着o2。是以兩個想成都無法繼續執行下去,就造成了死鎖。

然後,我們使用jstack來看一下線程堆棧資訊:

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

Found one Java-level deadlock:

=============================

"Thread-1":

waiting to lock monitor 0x00007f0134003ae8 (object 0x00000007d6aa2c98, a java.lang.Object),

which is held by "Thread-0"

"Thread-0":

waiting to lock monitor 0x00007f0134006168 (object 0x00000007d6aa2ca8, a java.lang.Object),

which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

at javaCommand.DeadLockclass.run(JStackDemo.java:40)

- waiting to lock <0x00000007d6aa2c98> (a java.lang.Object)

- locked <0x00000007d6aa2ca8> (a java.lang.Object)

at java.lang.Thread.run(Thread.java:745)

"Thread-0":

at javaCommand.DeadLockclass.run(JStackDemo.java:27)

- waiting to lock <0x00000007d6aa2ca8> (a java.lang.Object)

- locked <0x00000007d6aa2c98> (a java.lang.Object)

at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

linux列印jvm記憶體堆棧_java指令--jstack 工具 檢視JVM堆棧資訊

哈哈,堆棧寫的很明顯,它告訴我們 Found one Java-level deadlock,然後指出造成死鎖的兩個線程的内容。然後,又通過 Java stack information for the threads listed above來顯示更詳細的死鎖的資訊。 他說

Thread-1在想要執行第40行的時候,目前鎖住了資源<0x00000007d6aa2ca8>,但是他在等待資源<0x00000007d6aa2c98>Thread-0在想要執行第27行的時候,目前鎖住了資源<0x00000007d6aa2c98>,但是他在等待資源<0x00000007d6aa2ca8> 由于這兩個線程都持有資源,并且都需要對方的資源,是以造成了死鎖。 原因我們找到了,就可以具體問題具體分析,解決這個死鎖了。

其他

虛拟機執行Full GC時,會阻塞所有的使用者線程。是以,即時擷取到同步鎖的線程也有可能被阻塞。 在檢視線程Dump時,首先檢視記憶體使用情況。

對于jstack做的ThreadDump的棧,可以反映如下資訊(源自):

如果某個相同的call stack經常出現, 我們有80%的以上的理由确定這個代碼存在性能問題(讀網絡的部分除外);

如果相同的call stack出現在同一個線程上(tid)上, 我們很很大理由相信, 這段代碼可能存在較多的循環或者死循環;

如果某call stack經常出現, 并且裡面帶有lock,請檢查一下這個lock的産生的原因, 可能是全局lock造成了性能問題;

在一個不大壓力的群集裡(w<2), 我們是很少拿到帶有業務代碼的stack的, 并且一般在一個完整stack中, 最多隻有1-2業務代碼的stack,

如果經常出現, 一定要檢查代碼, 是否出現性能問題。

如果你懷疑有dead lock問題, 那麼請把所有的lock id找出來,看看是不是出現重複的lock id。

jstack -m 會列印出JVM堆棧資訊,涉及C、C++部分代碼,可能需要配合gdb指令來分析。

頻繁GC問題或記憶體溢出問題

一、使用jps檢視線程ID

二、使用jstat -gc 3331 250 20 檢視gc情況,一般比較關注PERM區的情況,檢視GC的增長情況。

三、使用jstat -gccause:額外輸出上次GC原因

四、使用jmap -dump:format=b,file=heapDump 3331生成堆轉儲檔案

五、使用jhat或者可視化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)分析堆情況。

六、結合代碼解決記憶體溢出或洩露問題。

死鎖問題

一、使用jps檢視線程ID

二、使用jstack 3331:檢視線程情況

主要參考資料: