讀者看見打印堆棧
可能會(huì)比較疑惑為什么要打印堆棧,不是調(diào)試的時(shí)候能看見堆棧信息么
澈缺,那我先列舉如下的兩個(gè)場景:
場景展示
目標(biāo)方法如下(可以先不看):
public void acceptTask(Player player, int modelId, boolean isLogin) {
log.error(TaskHelp.getPlayerInfo(player)+"開始接日常任務(wù):"+modelId + " isLogin: "+isLogin);
//省略其他代碼
}
下面的場景借上面的那個(gè)方法來展開說明
場景一:
- 小明在本地開發(fā)游戲時(shí)出現(xiàn)一個(gè)問題(即玩家在請求一鍵完成任務(wù)的時(shí)候怎憋,最后還有一個(gè)任務(wù)沒有完成)淌哟,查閱日志(即目標(biāo)方法唯一粘貼的語句)發(fā)現(xiàn)原因就是
acceptTask
方法被不正當(dāng)?shù)恼{(diào)用了一次,那個(gè)沒有完成的任務(wù)就是因?yàn)槎嘟尤×艘淮巍?/li> - 小明說好吧拗小,那我看看這個(gè)方法被哪些地方調(diào)用了,結(jié)果一看樱哼,媽呀哀九,幾十處被調(diào)用的地方。小明本意是想著如果只有這么一兩處調(diào)用唇礁,直接看看排查一下就行了的勾栗。幾十處有點(diǎn)多。
- 小明心想我調(diào)試一下看看堆棧就知道了盏筐,于是斷點(diǎn)就設(shè)置在
acceptTask
方法的第一行围俘,程序一運(yùn)行,誒不對啊琢融,斷點(diǎn)都進(jìn)來幾十次了界牡,還是沒有發(fā)現(xiàn)一些異常堆棧信息。
場景二:
- 還是同樣的問題出現(xiàn)在了正式的運(yùn)行的服務(wù)器上面漾抬,沒法調(diào)試宿亡。
問題解決
哈哈,‘小明’就是我自己咯纳令,只是不好意思當(dāng)時(shí)查找這個(gè)問題費(fèi)了很多時(shí)間挽荠。
好了言歸正傳克胳,我以前只注意到在我們程序報(bào)錯(cuò)的時(shí)候會(huì)打印錯(cuò)誤堆棧信息。但是我的程序沒有報(bào)錯(cuò)圈匆,而是邏輯錯(cuò)了漠另,于是我就google了一下,我們可以通過如下的方法來打印調(diào)用到此處的線程的目前的堆棧信息跃赚。
public void acceptTask(Player player, int modelId, boolean isLogin) {
log.error(TaskHelp.getPlayerInfo(player)+"開始接日常任務(wù):"+modelId + " isLogin: "+isLogin);
Thread.dumpStack();
}
其實(shí)就一行還是JDKThread
自帶的笆搓,Thread.dumpStack();
我的那處出問題的邏輯就是這樣發(fā)現(xiàn)的。打印的信息類似異常堆棧信息纬傲,在控制臺(tái)是紅色的比較醒目满败。
善后處理
進(jìn)入dumpStack
內(nèi)部:
public static void dumpStack() {
new Exception("Stack trace").printStackTrace();
}
其實(shí)這個(gè)和我們的異常堆棧信息打印是一樣的:
try {
//邏輯代碼
} catch (Exception e) {
e.printStackTrace(System.out);
}
只是dumpStack
的Exception
對象是我們自己new
的,而異常堆棧是JVM創(chuàng)建的叹括,所以在問題查找到以后應(yīng)該及時(shí)清理掉這處代碼算墨,避免無用對象的創(chuàng)建和回收。
總結(jié)
什么時(shí)候我們應(yīng)該打印堆棧领猾?
- 類似場景一米同,那種即便是可以調(diào)試,但是干擾消息太多的情況下摔竿。
- 高并發(fā)的場景真的不太好調(diào)試的時(shí)候面粮。
- 類似場景二,無法調(diào)試继低,如果目標(biāo)代碼可以熱加載熬苍,那就加上
Thread.dumpStack();
,然后再加載一次。
PS:其他一些需要堆棧信息的情況袁翁?
- 運(yùn)行中的程序出現(xiàn)了死鎖柴底,死循環(huán),這些情況線程幾乎是靜止的(就是出問題的線程沒有棧幀的壓入和壓出)粱胜。這些情況就比較好辦直接使用jvm的
jstack
工具就行了柄驻,這兒我打算寫一篇文章,以后寫好了焙压,鏈接過來鸿脓。