轉貼:http://my.oschina.net/flashsword/blog/205266
本文是一次線上OOM故障排查的經過,內容比較基礎但是真實,主要是記錄一下,沒有OOM排查經驗的同學也可以參考。
現象
我們之前有一個計算作業。最近經常出現不穩定,無法正常響應的情況。具體表現是:各種連接超時,從mysql、mongodb和zookeeper到netty,能超時的都超時過了。其他看不到太多有效的異常。
所以我們首先懷疑的是網絡問題,打電話跟運維確認,運維說網絡問題的可能性幾乎為0,因為我們的機器是虛機,宿主機上的其他設備都運轉正常。程序問題的可能性更大。繼續從應用日志和tomcat的catalina.out中查找日志,發現有一些OutOfMemoryError異常。實際上,出現這個異常就代表內存不夠了。
我們使用cat(公司的Java監控平台,已開源https://github.com/dianping/cat)查看堆使用的情況,看到如下的東西:
Memory Free已經接近了0,同時產生了大量的fullgc。
回到之前的連接timeout,我們知道,Java的連接timeout,除了網絡傳輸的時間,也包括了Java程序處理的時間,所以OOM導致timeout也不奇怪了。
工具和排查
之前JVM分析做的很少,在同事的幫助下,結合一點資料,完成了基本的分析。
首先可用的是
1 |
jmap -histo PID |
這個命令會將內存中最終保存的對象列出來。
其中”[“表示數組,例如”[B"是byte[],具體可以看Class.getName()
的Javadoc。
但是這個只能粗略定位原因,如果要仔細分析,需要知道是哪些個對象持有了它,這個時候,就需要dump內存下來,再離線分析了。
dump內存的命令是:
1 |
jmap -dump:format=b,file=/home/admin/dump.bin PID |
此操作異常耗時,我跟運維在假死的機器上嘗試了幾次,竟然把tomcat進程干掉了,使用時還是小心為妙…跟同事討論,認為jmap -dump
實際上也是往運行的JVM實例發送一個dump請求,所以如果實例內存不足,dump很可能會失敗。比較好的做法是先降低一部分負載(比如把線上的機器先切下線)再試。
我這里使用VisualVM
進行分析,大致效果如下:
這里選擇“計算保留大小”。這個保留大小是遞歸計算實例之間的依賴,得到的總大小。因為去掉了循環依賴,所以並不完全准確,但是用於排查夠了。選擇保留大小最大的實例,一般就是罪魁禍首了!
最后排查出的結果,是公司的RPC中間件使用了ThreadLocal來保存一個context,但是最后卻沒有釋放。按照架構組的說明,升級了版本,問題解決!