Java效能調優工具——Jstack
一、命令說明
Jstack是Jdk自帶的執行緒跟蹤工具,用於列印指定Java程序的執行緒堆疊資訊。
二、引數說明
jstack -l [pid]
注意:windows環境只支援這麼一個引數
三、使用示例
jstack -l 5524 > c:\users\Administrator\Desktop\jstack.txt
2018-09-14 12:59:46
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):
"ActiveMQ InactivityMonitor Worker" #528 daemon prio=5 os_prio=0 tid=0x000000001ce03800 nid=0x1bc8 waiting on condition [0x000000003dadf000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000071cf5ea58> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent .locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java :941)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
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
"ActiveMQ InactivityMonitor Worker" #527 daemon prio=5 os_prio=0 tid=0x000000001ce05800 nid=0x22c8 waiting on condition [0x000000003f56e000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000071cf5ea58> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
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
.........
執行緒狀態
想要通過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之間關係,以 及執行緒的狀態轉換圖:
進入區(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 <地址> 目標 :需與堆疊中的”parking to wait for (atjava.util.concurrent.SynchronousQueue$TransferStack)”結合來看。first–>此執行緒是在等待某個條件的發生,來把自己喚醒,second–>SynchronousQueue不是一個佇列,其是執行緒之間移交資訊的機制,當我們把一個元素放入到 SynchronousQueue 中時必須有另一個執行緒正在等待接受移交的任務,因此這就是本執行緒在等待的條件。
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)
四、Windows上如何檢視耗CPU最高的執行緒
使用windows工具ProcessExplorer來獲取對應的執行緒資訊,下面這個帖子講解的實在是太詳細了,請需要的同學移步下面連結
五、Linux上如何檢視耗CPU最高的執行緒
這個由於不想安裝Linux虛擬機器整環境了,請需要的同學直接非同步下面連結
六、參考連結
七、關於JDK和JRE的一個疑惑
1 . 為什麼執行環境使用JRE,而不是直接使用JDK?
2 . 如果系統有JVM監控相關的需求的話,是否可以考慮直接JDK?
如果有幸看到這個問題,還請留言,謝謝!