如top -H看到的消耗cpu的線程是不斷變化的,就比較麻煩了,有個同學寫了個腳本自動的去通過top -H看到的消耗cpu的線程找到對應的Java線程堆棧,在這種情況下可以用這個腳本去試試,如果看到的線程堆棧確實是比較耗cpu的動作,則基本可以定位到。
如仍然看不出,則可以嘗試多jstack看看,然后多看看是否經常有一些耗cpu的動作在不同的線程不斷的出現。
如可使用perf,則可用perf top看看cpu消耗的熱點,不過默認的版本上只能看到jit后的代碼,因此可能會比較難對應到具體的代碼,這里有一個基于perf排查的Java應用cpu us詭異現象的case。
總結來說,cpu us消耗高的問題排查還是有一定復雜性,例如之前我碰到過反序列化的對象比較大,請求又非常頻繁,導致cpu us消耗增高了很多,但當時的機器內核版本不夠,不支持perf,從jstack等等上都看不出什么,后來是由于從業務監控的變化上才排查出問題。
內存問題
盡管JVM是自動管理內存的分配和回收的,但Java程序員們還是會經常碰到各種各樣的內存問題。
最常見的第一個問題是java.lang.OutOfMemoryError,估計寫Java的同學都碰到過。
在日志中可能會看到java.lang.OutOfMemoryError: Unable to create new native thread,可以先統計下目前的線程數(例如ps -eLf | grep java -c),然后可以看看ulimit -u的限制值是多少,如線程數已經達到限制值,如限制值可調整,則可通過調整限制值來解決;如不能調限制值,或者創建的線程已經很多了,那就需要看看線程都是哪里創建出來的,同樣可通過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.Thread", method="start" ) public static void traceExecute(){ jstack(); } } |
在找到是哪里創建造成了后,之后就可以想辦法解決了,例如這種情況下常見的有可能是用了Executors.newCachedThreadPool這種來創建了一個沒限制大小的線程池。
還有一種可能是ulimit -u的限制還沒到,內存也空閑,但仍然創建不了,這有可能是由于在2.6.18/32內核上kernel.pid_max默認的32768造成的,這個值其實直接限制了最多能創建的線程數就是32768(即使ulimit -u的值比這大也沒用)。
java.lang.OutOfMemoryError: Heap Size或GC overhead limit exceeded也是常見的現象,在出現了這兩種現象的情況下,最重要的是dump出內存,一種方法是通過在啟動參數上增加-XX:+HeapDumpOnOutOfMemoryError,另一種方法是在當出現OOM時,通過jmap -dump獲取到內存dump,在獲取到內存dump文件后,可通過MAT進行分析,但通常來說僅僅靠MAT可能還不能直接定位到具體應用代碼中哪個部分造成的問題,例如MAT有可能看到是某個線程創建了很大的ArrayList,但這樣是不足以解決問題的,所以通常還需要借助btrace來定位到具體的代碼,可以看看這兩個OOM排查的case。
原文轉自:http://bluedavy.me/?p=445