java進程占用CPU或者內存高問題排查


排查步驟:

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的狀態,跟操作系統中的線程狀態無關。

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM