排查步驟:
1. 使用top命令查看系統資源的使用情況,命令:top
如圖:排行前面的就是占用資源最多的
2.定位線程問題
方法一: top -Hp 查看線程 :使用命令 top -Hp 12183
,這個時候它會把這個進程里面所有的線程全部線程都羅列出來,這些都是Java這個進程里面內部的一些線程,如下圖所示:
方法二:ps 命令:ps p 12183 -L -o pcpu,pmem,pid,tid,time,tname,cmd
3. 進制轉換
根據第二部找到的進程對應的線程,隨機挑選一個作為例子,例如:12184;
因為 jstack 用到的線程號是16進制的,所以我們需要把112191 的10進制轉換成16進制才可以。命令:printf "%x\n" 12184
4. 打印進程為堆棧
將進程PID為12183 的堆棧信息打印到jstack.log中,命令:jstack -l 12183 > jstack.log
5.查看堆棧信息 命令:vim jstack.log
搜索第三步轉換的十六進制線程名稱,即可看到線程運行相關信息。
線程信息各部分介紹:
"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線程狀態:這里標明了線程在代碼級別的狀態,詳細的內容見下面的 JVM線程運行狀態 部分。- 線程調用棧信息:下面就是當前線程調用的詳細棧信息,用於代碼的分析。堆棧信息應該從下向上解讀,因為程序調用的順序是從下向上的。
系統線程狀態 (Native Thread Status)
系統線程有如下狀態:
deadlock
死鎖線程,一般指多個線程調用期間進入了相互資源占用,導致一直等待無法釋放的情況。
runnable
一般指該線程正在執行狀態中,該線程占用了資源,正在處理某個操作,如通過SQL語句查詢數據庫、對某個文件進行寫入等。
blocked
線程正處於阻塞狀態,指當前線程執行過程中,所需要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識為阻塞狀態,可以理解為等待資源超時的線程。
waiting on condition
線程正處於等待資源或等待某個條件的發生,具體的原因需要結合下面堆棧信息進行分析。
(1)如果堆棧信息明確是應用代碼,則證明該線程正在等待資源,一般是大量讀取某種資源且該資源采用了資源鎖的情況下,線程進入等待狀態,等待資源的讀取,或者正在等待其他線程的執行等。
(2)如果發現有大量的線程都正處於這種狀態,並且堆棧信息中得知正等待網絡讀寫,這是因為網絡阻塞導致線程無法執行,很有可能是一個網絡瓶頸的征兆:
-
- 網絡非常繁忙,幾乎消耗了所有的帶寬,仍然有大量數據等待網絡讀寫;
- 網絡可能是空閑的,但由於路由或防火牆等原因,導致包無法正常到達;
所以一定要結合系統的一些性能觀察工具進行綜合分析,比如netstat統計單位時間的發送包的數量,看是否很明顯超過了所在網絡帶寬的限制;觀察CPU的利用率,看系統態的CPU時間是否明顯大於用戶態的CPU時間。這些都指向由於網絡帶寬所限導致的網絡瓶頸。
(3)還有一種常見的情況是該線程在 sleep,等待 sleep 的時間到了,將被喚醒。
waiting for monitor entry 或 in Object.wait()
Moniter 是Java中用以實現線程之間的互斥與協作的主要手段,它可以看成是對象或者class的鎖,每個對象都有,也僅有一個 Monitor。
從上圖可以看出,每個Monitor在某個時刻只能被一個線程擁有,該線程就是 "Active Thread",而其他線程都是 "Waiting Thread",分別在兩個隊列 "Entry Set"和"Waint Set"里面等待。其中在 "Entry Set" 中等待的線程狀態是 waiting for monitor entry
,在 "Wait Set" 中等待的線程狀態是 in Object.wait()
。
JVM線程運行狀態 (JVM Thread Status)
在 java.lang.Thread.State 中定義了線程的狀態:
NEW
至今尚未啟動的線程的狀態。線程剛被創建,但尚未啟動。
RUNNABLE
可運行線程的線程狀態。線程正在JVM中執行,有可能在等待操作系統中的其他資源,比如處理器。
BLOCKED
受阻塞並且正在等待監視器的某一線程的線程狀態。處於受阻塞狀態的某一線程正在等待監視器鎖,以便進入一個同步的塊/方法,或者在調用 Object.wait 之后再次進入同步的塊/方法。
在Thread Dump日志中通常顯示為 java.lang.Thread.State: BLOCKED (on object monitor) 。
WAITING
某一等待線程的線程狀態。線程正在無期限地等待另一個線程來執行某一個特定的操作,線程因為調用下面的方法之一而處於等待狀態:
-
- 不帶超時的 Object.wait 方法,日志中顯示為 java.lang.Thread.State: WAITING (on object monitor)
- 不帶超時的 Thread.join 方法
- LockSupport.park 方法,日志中顯示為 java.lang.Thread.State: WAITING (parking)
TIMED_WAITING
指定了等待時間的某一等待線程的線程狀態。線程正在等待另一個線程來執行某一個特定的操作,並設定了指定等待的時間,線程因為調用下面的方法之一而處於定時等待狀態:
-
- Thread.sleep 方法
- 指定超時值的 Object.wait 方法
- 指定超時值的 Thread.join 方法
- LockSupport.parkNanos
- LockSupport.parkUntil
TERMINATED
線程處於終止狀態。
根據Java Doc中的說明,在給定的時間上,一個只能處於上述的一種狀態之中,並且這些狀態都是JVM的狀態,跟操作系統中的線程狀態無關。