Java七武器系列霸王槍 -- 線程狀態(tài)分析jstack
在應(yīng)用運行過程中,除了遇到 Bug,執(zhí)行結(jié)果不符合我們的預(yù)期之外,還有些時候是應(yīng)用的響應(yīng)問題。
比如同樣對于頁面的請求,正常情況下兩秒之內(nèi)就返回了結(jié)果,頁面已經(jīng)渲染完成展現(xiàn)出來了。
那不正常的時候,就會看到頁面請求在不斷的加載中,沒有拿到響應(yīng)。當然這個時候可以分析的點有很多:
- 可能操作系統(tǒng)的CPU、內(nèi)存等資源占用比較多;
- 也有可能是應(yīng)用內(nèi)線程較多在等待;
- 還有可能是涉及到讀數(shù)據(jù)庫等操作時遇到鎖之類的問題。
這次先不理會操作系統(tǒng)層面上的東西,只來分析應(yīng)用內(nèi)可能遇到的情況。
回到前面說的問題,正常情況兩秒鐘返回了,這次一分鐘還沒返回。這個時候,關(guān)注應(yīng)用內(nèi)的話,就需要分析一下其線程執(zhí)行情況,來了解了定位具體問題。
此時,這次要提到的這個「武器」jstack 可以快速定位,直達患處。
是什么
jstac 是什么? 這個是 Oracle JDK 默認包含的一個用于打印執(zhí)行 Java 進程的當前線程棧信息的工具。官方是這樣介紹的:
jstack prints Java stack traces of Java threads for a given Java process or core file or a remote debug server. For each Java frame, the full class name, method name, 'bci' (byte code index) and line number, if available, are printed. |
注意其中幾個關(guān)鍵點:每一個 Java Frame 的全類名,方法名,如果能拿到行號的話還會顯示行號??催^前面介紹調(diào)試技巧那篇文章(80%的程序員都不了解的調(diào)試技巧)的朋友可能還記得,其中有一個功能是Drop Frame, 來實現(xiàn)后退執(zhí)行。和這里的是一個地方,都對應(yīng)線程中的一級調(diào)用。
使用 jstack 打出來的信息,和一般應(yīng)用遇到異常時的printStackTrace 基本一樣,只是那只是一個線程調(diào)用鏈的,這里通過工具,可以把應(yīng)用內(nèi)所有線程都打出來。
用法
使用方式和一般的 Java 分析工具類似,都是通過
命令名 <可選參數(shù)> + pid(進程id) |
這種格式使用。比如對于 jstack, 一般可以直接 jstack 應(yīng)用pid 即可。這里 pid 可以通過Java的 jps 工具獲取,也可以通過 Linux 下的ps 工具和 Windows 下的任務(wù)管理器獲取。
輸出
我們以一個Tomcat進程為例,輸出類似這樣:
我們看上面的幾個框:
最上方左側(cè),是當前線程的線程名稱,可以根據(jù)此來在應(yīng)用內(nèi)大量的線程中找到我們關(guān)心的線程正在執(zhí)行的操作。例如 Tomcat 一般 http-port -x 這種線程是請求的處理線程,頁面響應(yīng)慢的時候,可以直接找這一類線程。
隨著請求的增多,線程數(shù)也會很多。所以一般多線程應(yīng)用開發(fā),一個好的實戰(zhàn)建議是為創(chuàng)建的線程起一個有意義的名字,否則打出來的 stack 里大量的 thread -1, thread -2 這種,天曉得哪一個才是你的。
第二行的框內(nèi)內(nèi)容,表示當前線程的執(zhí)行狀態(tài),是運行狀態(tài)還是TIME_WAITING,還是等待鎖等,可以根據(jù)線程狀態(tài)來了解。
第三個大框中的內(nèi)容就和我們異常時輸出的 stackTrace 一樣,是當前代碼的調(diào)用鏈。
第四個框中的內(nèi)容,是當前線程掛的鎖的情況。
上面的截圖,是沒有鎖互相占用的情況下的輸出。如果一個多線程中有鎖等待時,會有類似這樣的輸出:
注意,此時線程狀態(tài)變成了 BLOCKED, 同時,在線程的調(diào)用鏈中,有一個waiting to lock 的輸出, 同時,在下方持胡鎖的線程中,會有一個 lock xxx,這個是當前鎖對象,通過這個就可以看出當前還有哪些線程在等待同一個鎖。
所以回到前面的問題,如果此時因為鎖占用導(dǎo)致的,可以從輸出中看到,同時如果是數(shù)據(jù)庫連接池滿了,線程就會停在數(shù)據(jù)庫連接的操作上,在 stackTrace 中一眼就能看的出來,甚至網(wǎng)絡(luò) Socket 讀取之類的在等待,都會在調(diào)用鏈中體現(xiàn)出來,從而可以快速的定位問題,解決問題。
那對于應(yīng)用的觀察分析,還需要注意的是,可以間隔幾秒鐘時間執(zhí)行一下 jstack,分析一下輸出,然后對比一下幾次輸出的區(qū)別,看看這一段時間內(nèi)應(yīng)用有哪些地方在執(zhí)行,分析和解決問題。
【本文為51CTO專欄作者“侯樹成”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號『Tomcat那些事兒』獲取授權(quán)】