以下是Java應用在運行時常見的一些問題,總結了運行時黑盒方式的一些排查方法,也希望看到的同學能給予補充,無論是補充碰到的問題,還是補充解決方法。
類裝載的相關問題
寫過Java代碼的同學估計都碰到過ClassNotFoundException/NoClassDefFoundError/NoSuchMethodException(還有一個常見的ClassCastException就不在這里說了)。
當碰到ClassNotFoundException/NoClassDefFound時,如果很確定這個class應該是從哪個路徑裝載的,則可以去相應的路徑找下是否有對應的class文件存在,例如web應用通常會在*.war(ear)/WEB-INF/lib或classes目錄下,對于lib下的jar包,可通過寫個小腳本jar -tvf的方式找找;
如不確定class是從哪裝載的,則可以先看看日志里是否有堆棧信息,如果有的話則可以看到具體是哪個ClassLoader實現在裝載class,之后則可以通過www.grepcode.com或jar包反編譯(推薦一個挺好用的反編譯工具)看看具體是從哪裝載的class;
如日志中沒有,則可以用btrace來跟蹤下拋出以上兩個異常的堆棧信息,btrace腳本類似如下:
?
1
2
3
4
5
6
7
8
9
10
11
|
import static com.sun.btrace.BTraceUtils.*; import com.sun.btrace.annotations.*; @BTrace public class Trace{ @OnMethod( clazz="java.lang.ClassNotFoundException", method="<init>" ) public static void traceExecute(){ jstack(); } } |
拿到堆棧信息后,可以繼續使用上面的方法進行排查,在確認了class裝載的位置后,則可將相應的class/jar加上即可。
這里還有個NoClassDefFoundError排查的case,感興趣的話可以看看。
當碰到NoSuchMethodException時,通常是由于不存在需要的class版本或class版本沖突造成的,在這種情況下,可通過在啟動參數上增加-XX:+TraceClassLoading,重啟后在日志里看看此class是在哪load的,然后可以在對應的路徑下用jar -tvf找找是不是有正確的版本的jar存在,通??赡軙l現是版本沖突造成的,對于版本沖突的問題通常需要刪掉有沖突的版本的jar,對于沒有正確版本的,則需要用正確版本的jar替換掉(當然,這種通常還會出現一些惡心的問題,例如和容器/框架的jar沖突等)。
cpu us消耗高
當出現cpu us消耗高時,通常的排查方法如下。
從經驗上來說,有些時候是由于頻繁cms gc或fgc造成的(頻繁的意思是差不多每次cms gc或fgc一結束后又立刻繼續),在gc log是記錄的情況下(-Xloggc:),可通過gc log看看,如果沒打開gc log,可通過jstat -gcutil來查看,如是gc頻繁造成的,則可跳到后面的內存問題 | GC頻繁部分看排查方法。
如不是上面的原因,可使用top -H查看線程的cpu消耗狀況,這里有可能會看到有個別線程是cpu消耗的主體,這種情況通常會比較好解決,可根據top看到的線程id進行十六進制的轉換,用轉換出來的值和jstack出來的java線程堆棧的nid=0x[十六進制的線程id]進行關聯,即可看到此線程到底在做什么動作,這個時候需要進一步的去排查到底是什么原因造成的,例如有可能是正則計算,有可能是很深的遞歸或循環,也有可能是錯誤的在并發場景使用HashMap等,例如這里還有一段隨即生成字符串的耗cpu的代碼case。
原文轉自:http://bluedavy.me/?p=445