https://blog.csdn.net/qq_16681169/article/details/53296137
一.出現問題
在前一段時間日常環境很不穩定,前端調用mtop接口會出網絡異常或服務不存在的異常。查詢了服務器上的HSF會有偶爾掛死的情況,服務器上的接口服務都不可用。於是我們對服務器上的狀況進行了排查。
二.排查問題的過程
在這次的問題排查主要是圍繞JVM的內存使用情況,生成對象分布情況以及GC情況來討論的。中間有一些細節一開始存有疑問,迷霧的排除不算太順利。首先要感謝下基礎架構事業群的右席,井桐,梁希,坤谷 ,螞蟻的寒泉子,中間技術部的思邪,望淘以及我們部的行默,今為,張霸,常曉師兄的一起討論與問題排查的推動。為了寫一篇比較干貨,看了之后能立即上手開始排查一些類似問題的論文,我廢話不多說,寫一篇忽略掉曲折過程,直達終點的BLOG,有不當之處歡迎看官指出 :)
三.排查問題的步驟
1.查看服務器大概情況
首先最容易想到的是top命令,它能夠實時顯示系統中各個進程的資源占用狀況,經常用來監控linux的系統狀況,比如cpu、內存的使用。我上下當時截圖的狀況

由於前段時間服務不可用了,於是我們重啟了ali-tomcat服務,發現Java進程重啟之后最顯著的問題就是占用內存挺多,虛擬內存總量占了5.5G+,43.2%。
2.查看Java進程突然不服務的原因
先查看過了一些業務日志與關系緊密的中間件,組件(比如HSF,Mtop等)的日志,無明顯歷史錯誤。於是我們在考慮Java進程突然不可用的原因。這里推薦一個很實用的命令:dmesg。dmesg可以用來查看開機之后的系統日志,其中可以捕捉到一些系統資源與進程的變化信息。dmesg |grep -E ‘kill|oom|out of memory’ 來搜索內存溢出的信息挺實用。我們這次就是用來這個命令查出來是內存溢出的原因。上圖
由圖可見,內存不足,Java進程被殺死的案發現場清晰可見。
3. 查看JVM狀態
關於OOM出現的情況,一般可以猜想是內存泄露,或者是加載了過多class或者創建了過多對象,給JVM分配的內存不夠導致。於是我們用一下常用的JDK自帶工具來觀察下JVM的狀態:
1. ps -aux|grep java 當服務重新部署后,可以找出當前Java進程的PID
2. jstat -gcutil pid interval 用於查看當前GC的狀態,它對Java應用程序的資源和性能進行實時的命令行的監控,包括了對Heap size和垃圾回收狀況的監控。
圖中可見,Full GC次數遠大於Young GC 。由此可見可能是老年代空間大小不足,導致應用需要頻繁Full GC,因為Full GC要將新生代、舊生代、持久代一起進行GC。
3. jmap -histo:live pid 可用統計存活對象的分布情況,從高到低查看占據內存最多的對象。上圖
由圖上看,Java進程top時發現占用虛擬內存5.5G,而byte[]數組占用了3G。很有可能它就是凶手!
4.Java dump分析問題
Java dump,也叫做 Thread dump,是 JVM 故障診斷中最重要的轉儲文件之一。JVM 的許多問題都可以使用這個文件進行診斷,其中比較典型的包括線程阻塞,CPU 使用率過高,JVM Crash,堆內存不足,和類裝載等問題。
1. jmap -dump:format=b,file=文件名 [pid] 利用Jmap dump,但是執行時出現意外了,如圖所示
在基礎架構事業群同學的提示下,發現這是JDK 7的Jmap 的Bug,stack over flow 中有這個問題的回答文檔鏈接
2. gcore 出現了這個問題后,再其他同學的提醒下,我們嘗試了gcore。在排查問題的時候,對於保留現場信息的操作,可以用gcore [pid]直接保留內存信息,這個的執行速度會比jmap -dump快不少,之后可以再用jmap/jstack等從core dump文件里提取相應的信息。
- a 先生成core dump 主要命令有
sudo gdb -q --pid //啟動gdb命令 (gdb) generate-core-file //這里調用命令生成gcore的dump文件 (gdb) gcore /tmp/jvm.core //dump出core文件 (gdb) detach //detach是用來斷開與jvm的連接的 (gdb) quit //退出
- 1
- 2
- 3
- 4
- 5
-
b 把core文件轉換成hprof
jmap -dump:format=b,file=heap.hprof /opt/taobao/java/bin /tmp/jvm.core
這樣就可以使用可視化的內存分析工具來一探究竟了 -
c 使用性能分析工具對hprof進行分析
這里主要使用了2個工具:一個是 MAT,一個基於Eclipse的內存分析工具,它可以快速的計算出在內存中對象的占用大小,看看是誰阻止了垃圾收集器的回收工作,並可以通過報表直觀的查看到可能造成這種結果的對象。第二個是zprofile(阿里內部工具)。直接上圖
MAT上的圖
![]()
zprofile上的圖
![]()
通過圖中我們可以發現ali-tomcat的StandardClassLoader類加載器的Retained Size(當前對象大小+當前對象可直接或間接引用到的對象的大小總和)占用了內存的44.21%。並且類加載器的個數高達3212個。於是我們推測可能是ali-tomcat的StandardClassLoader的類加載時出了問題,導致引入的byte[]數組占用的堆大小過多,而Full GC回收不過來,導致了OOM。
5.通過tomcat查明真相
因為懷疑問題出現在StandardClassLoader,於是我們去查看了tomcat的日志,在Catalina引擎日志文件catalina.log找到了一些異樣的報警和報錯:
上面提示加載類時可能會有內存泄露。然后在tomcat的類加載過程中還出現了加載javassist.jar包出現了EOFException。后來拉進了中間件部門ali-tomcat的開發人員一看,原來是我們那個版本的ali-tomcat的會出現這樣的問題,要升級新的版本問題就能解決。


