1. 程式人生 > >通過 thread dump 分析找到高CPU耗用與記憶體溢位的Java程式碼

通過 thread dump 分析找到高CPU耗用與記憶體溢位的Java程式碼

但實際上,我們是可以幫助他們的,效果好的話還可以定位到具體出問題的程式碼行數,思路如下:
1.通過對CPU與記憶體的耗用情況判斷是否存在問題;
2.通過top命令找到可疑的執行緒的ID;
3.確認應用伺服器的console資訊的輸出位置;
4.通過kill -3 ID 的方式獲取thread dump資訊;
5.備份console日誌,並通過執行緒ID檢查與程式碼相關的資訊。

下面,我們開始具體的操作過程:

1.通過對CPU與記憶體的耗用情況判斷是否存在問題;
判斷CPU可以直接在top中檢視CPU的 %us 數值就可以了,通常在50%以下都算正常,一旦超過60%甚至高到80-90且穩居不下,那麼就肯定存在問題了;
判斷記憶體時需要注意的是,直接看第一行Mem:的used數值是不準確的,因為通常在執行一段時間後,這個值都會非常大,這是因為Linux對記憶體的使用理念“記憶體就是拿來用的”,它會將空閒的記憶體儘可能的用於快取,來提升效能,所以我們應該更多的參考 -/+ buffers/cache: 這一行的used數值,如果該值與第一行的used數值很接近且與記憶體總量相差很少的話,那麼記憶體才算處於緊張的狀態;
虛擬記憶體的耗用也值得參考,Linux只有在記憶體確實不夠用的情況下才會大量使用虛擬記憶體,如果虛擬記憶體使用值為0或者100兆左右,那麼可以確定實體記憶體尚且充足;
系統負載的數值也很值得參考,即top中的 load average,通常小於 1 代表非常良好,1-5之間代表正常,大於5 則表明系統處於負載的狀態,超出的數值越大負載越大。

2.通過top命令找到可疑的執行緒的ID;
執行top命令,在這個Linux的“工作管理員”中輸入大寫的“H”來開啟執行緒模式,然後所有執行緒會預設以CPU耗用高低排序;
如果想以記憶體進行排序,再敲入大寫的“M”即可,其中“RES”看到的就是實體記憶體的耗用大小,但由於執行緒的共享記憶體原理,我們看到的每個執行緒的記憶體大小都和他們所屬程序是一樣的。

3.確認應用伺服器的console資訊的輸出位置;
通常來說,應用伺服器的console資訊都會輸出到log當中,比如Tomcat會輸出到catalina.out檔案中。
但jboss與weblogic就需要進行一些修改設定才行,其中:
jboss如果使用了官方的啟動指令碼,則需要修改啟動指令碼中的如下兩行中的 /dev/null,將其改為指定的log檔案位置,如 /opt/jboss_console.log,再重啟jboss即可。
---------------------------------------------------------------------
JBOSS_CONSOLE="/dev/null"

JBOSS_CONSOLE=${JBOSS_CONSOLE:-"/dev/null"}
---------------------------------------------------------------------

weblogic則需要在console管理介面中的“域結構”-“環境”-“區域”-“伺服器”-“AdminServer”-“日誌記錄”-“高階”- 勾選“已啟動重定向標準輸出日誌記錄”,再點選“啟用更改”並重啟weblogic即可。然後所有的console資訊都會輸出到logs目錄下的AdminServer.log檔案中。

4.通過kill -3 ID 的方式獲取thread dump資訊;


確認了應用伺服器的console資訊輸出,並找到可疑的執行緒ID之後,執行 kill -3 ID 即可將該執行緒這一刻的所有thread dump資訊輸出到log當中。
建議多執行幾次該命令,以便獲取充足的資訊。

5.備份console日誌,並通過執行緒ID檢查與程式碼相關的資訊。
執行了thread dump資訊輸出之後,立刻使用cp命令備份log檔案;
將log檔案下載到本地,將程序ID換算成為十六進位制,比如 29433 換算為十六進位制就是 72f9;
開啟log檔案(建議使用UE或Notepad++等編輯器),搜尋包含 72f9 的相關資訊,然後仔細檢查相關程式碼。

以我的實際情況為例:
下面這一段資訊就包含了“nid=0x72f9”,表明與程序29433相關,所提到的 JIoEndpoint.java 則是實際的程式碼檔案,後面的 442 則代表具體的行數。
---------------------------------------------------------------------
"http-0.0.0.0-80-78" daemon prio=10 tid=0x00000000686db800 nid=0x72f9 in Object.wait() [0x000000004cea6000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00002aab0bccd5b8> (a org.apache.tomcat.util.net.JIoEndpoint$Worker)
at java.lang.Object.wait(Object.java:485)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)
- locked <0x00002aab0bccd5b8> (a org.apache.tomcat.util.net.JIoEndpoint$Worker)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:442)
at java.lang.Thread.run(Thread.java:619)
---------------------------------------------------------------------

到此,實際的工作才剛剛開始,需要開發人員仔細檢查所有類似的資訊,然後找出有問題的程式碼。

有資料說,thread dump 的作用其實還有很多,可以從中發現很多應用程式的執行問題,比如死鎖等;且有一個Eclipse外掛的視覺化分析工具“lockness” 可以對該類日誌進行更專業的分析。