遇到java程序跑不動怎麼辦,jstack是比較容易想到的一個工具,利用jstack來dump出一個線程堆棧快照,而後具體分析。html
通常的堆棧大概是由下面的部分組成的:java
1 "resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000] 2 java.lang.Thread.State: WAITING (parking) 3 at sun.misc.Unsafe.park(Native Method) 4 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315) 5 at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196) 6 at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147) 7 at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118) 8 9 10 "Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000] 11 java.lang.Thread.State: TIMED_WAITING (on object monitor) 12 at java.lang.Object.wait(Native Method) 13 - waiting on <0x00000006f0620ff0> (a java.util.TaskQueue) 14 at java.util.TimerThread.mainLoop(Timer.java:552) 15 - locked <0x00000006f0620ff0> (a java.util.TaskQueue) 16 at java.util.TimerThread.run(Timer.java:505)
"resin-22129"
線程名稱:若是使用 java.lang.Thread 類生成一個線程的時候,線程名稱爲 Thread-(數字) 的形式,這裏是resin生成的線程;daemon
線程類型:線程分爲守護線程 (daemon) 和非守護線程 (non-daemon) 兩種,一般都是守護線程;prio=10
線程優先級:默認爲5,數字越大優先級越高;tid=0x00007fbe5c34e000
JVM線程的id:JVM內部線程的惟一標識,經過 java.lang.Thread.getId()獲取,一般用自增的方式實現;nid=0x4cb1
系統線程id:對應的系統線程id(Native Thread ID),能夠經過 top 命令進行查看,現場id是十六進制的形式;waiting on condition
系統線程狀態:這裏是系統的線程狀態;[0x00007fbe4ff7c000]
起始棧地址:線程堆棧調用的其實內存地址;java.lang.Thread.State: WAITING (parking)
JVM線程狀態:這裏標明瞭線程在代碼級別的狀態。系統線程有以下狀態:數據庫
死鎖線程,通常指多個線程調用期間進入了相互資源佔用,致使一直等待沒法釋放的狀況。api
通常指該線程正在執行狀態中,該線程佔用了資源,正在處理某個操做,如經過SQL語句查詢數據庫、對某個文件進行寫入等。網絡
線程正處於阻塞狀態,指當前線程執行過程當中,所須要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識爲阻塞狀態,能夠理解爲等待資源超時的線程。多線程
線程正處於等待資源或等待某個條件的發生,具體的緣由須要結合下面堆棧信息進行分析。oracle
(1)若是堆棧信息明確是應用代碼,則證實該線程正在等待資源,通常是大量讀取某種資源且該資源採用了資源鎖的狀況下,線程進入等待狀態,等待資源的讀取,或者正在等待其餘線程的執行等。app
(2)若是發現有大量的線程都正處於這種狀態,而且堆棧信息中得知正等待網絡讀寫,這是由於網絡阻塞致使線程沒法執行,頗有多是一個網絡瓶頸的徵兆:工具
因此必定要結合系統的一些性能觀察工具進行綜合分析,好比netstat統計單位時間的發送包的數量,看是否很明顯超過了所在網絡帶寬的限制;觀察CPU的利用率,看系統態的CPU時間是否明顯大於用戶態的CPU時間。這些都指向因爲網絡帶寬所限致使的網絡瓶頸。oop
(3)還有一種常見的狀況是該線程在 sleep,等待 sleep 的時間到了,將被喚醒。
Moniter 是Java中用以實現線程之間的互斥與協做的主要手段,它能夠當作是對象或者class的鎖,每一個對象都有,也僅有一個 Monitor。
從上圖能夠看出,每一個Monitor在某個時刻只能被一個線程擁有,該線程就是 "Active Thread",而其餘線程都是 "Waiting Thread",分別在兩個隊列 "Entry Set"和"Waint Set"裏面等待。其中在 "Entry Set" 中等待的線程狀態是 waiting for monitor entry
,在 "Wait Set" 中等待的線程狀態是 in Object.wait()
。
(1)"Entry Set"裏面的線程。
咱們稱被 synchronized
保護起來的代碼段爲臨界區
當一個線程申請進入臨界區時,它就進入了 "Entry Set" 隊列中,這時候有兩種可能性:
臨界區的設置是爲了保證其內部的代碼執行的原子性和完整性,但由於臨界區在任什麼時候間只容許線程串行經過,這和咱們使用多線程的初衷是相反的。若是在多線程程序中大量使用synchronized,或者不適當的使用它,會形成大量線程在臨界區的入口等待,形成系統的性能大幅降低。若是在Thread Dump中發現這個狀況,應該審視源碼並對其進行改進。
(2)"Wait Set"裏面的線程
當線程得到了Monitor,進入了臨界區以後,若是發現線程繼續運行的條件沒有知足,它則調用對象(一般是被synchronized的對象)的wait()方法,放棄Monitor,進入 "Wait Set"隊列。只有當別的線程在該對象上調用了 notify()或者notifyAll()方法,"Wait Set"隊列中的線程才獲得機會去競爭,可是隻有一個線程得到對象的Monitor,恢復到運行態。"Wait Set"中的線程在Thread Dump中顯示的狀態爲 in Object.wait()。一般來講,
一般來講,當CPU很忙的時候關注 Runnable 狀態的線程,反之則關注 waiting for monitor entry 狀態的線程。
在 java.lang.Thread.State 中定義了線程的狀態:
至今還沒有啓動的線程的狀態。線程剛被建立,但還沒有啓動。
可運行線程的線程狀態。線程正在JVM中執行,有可能在等待操做系統中的其餘資源,好比處理器。
受阻塞而且正在等待監視器的某一線程的線程狀態。處於受阻塞狀態的某一線程正在等待監視器鎖,以便進入一個同步的塊/方法,或者在調用 Object.wait 以後再次進入同步的塊/方法。
在Thread Dump日誌中一般顯示爲 java.lang.Thread.State: BLOCKED (on object monitor) 。
某一等待線程的線程狀態。線程正在無期限地等待另外一個線程來執行某一個特定的操做,線程由於調用下面的方法之一而處於等待狀態:
指定了等待時間的某一等待線程的線程狀態。線程正在等待另外一個線程來執行某一個特定的操做,並設定了指定等待的時間,線程由於調用下面的方法之一而處於定時等待狀態:
線程處於終止狀態。
根據Java Doc中的說明,在給定的時間上,一個只能處於上述的一種狀態之中,而且這些狀態都是JVM的狀態,跟操做系統中的線程狀態無關。
參考
https://go-on.iteye.com/blog/1673894
http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
https://www.javatang.com/archives/2017/10/25/36441958.html
https://www.cubrid.org/blog/the-principles-of-java-application-performance-tuning