生產出現oom問題,怎么排查?


1、使用dmesg命令查看系統日志

dmesg |grep -E ‘kill|oom|out of memory’,可以查看操作系統啟動后的系統日志,這里就是查看跟內存溢出相關聯的系統日志

 

2、這時候,需要啟動項目,使用ps命令查看進程

ps -aux|grep java命令查看一下你的java進程,就可以找到你的java進程的進程id

 

3、接着使用top命令

top命令顯示的結果列表中,會看到%MEM這一列,這里可以看到你的進程可能對內存的使用率特別高。以查看正在運行的進程和系統負載信息,包括cpu負載、內存使用、各個進程所占系統資源等

 

 

 

4、使用jstat命令

jstat -gcutil 20886 1000 10命令,就是用jstat工具,對指定java進程(20886就是進程id,通過ps -aux | grep java命令就能找到),按照指定間隔,看一下統計信息,這里會每隔一段時間顯示一下,包括新生代的兩個S0、s1區、Eden區,以及老年代的內存使用率,還有young gc以及full gc的次數。

 

 

 

使用 jstat -gcutil 8968 500 5 表示每500毫秒打印一次Java堆狀況(各個區的容量、使用容量、gc時間等信息),打印5次

 

 

 

例如:

看到的東西類似下面那樣:

S0       S1     E            O         YGC  FGC

26.80   0.00   10.50    89.90    86     954

 

其實如果大家了解原理,應該知道,一般來說大量的對象涌入內存,結果始終不能回收,會出現的情況就是,快速撐滿年輕代,然后young gc幾次,根本回收不了什么對象,導致survivor區根本放不下,然后大量對象涌入老年代。老年代很快也滿了,然后就頻繁full gc,但是也回收不掉。

然后對象持續增加不就oom了,內存放不下了,爆了唄。

所以jstat先看一下基本情況,馬上就能看出來,其實就是大量對象沒法回收,一直在內存里占據着,然后就差不多內存快爆了。

 

 

 

5、使用jmap命令查看

執行jmap -histo pid可以打印出當前堆中所有每個類的實例數量和內存占用,如下,class name是每個類的類名([B是byte類型,[C是char類型,[I是int類型),bytes是這個類的所有示例占用內存大小,instances是這個類的實例數量。

 

 

 

 

6、把當前堆內存的快照轉儲到dumpfile_jmap.hprof文件中,然后可以對內存快照進行分析

 

使用jmap -dump:format=b,file=文件名 [pid],就可以把指定java進程的堆內存快照搞到一個指定的文件里去,但是jmap -dump:format其實一般會比較慢一些,也可以用gcore工具來導出內存快照

 

例如:jmap -dump:format=b,file=D:/log/jvm/dumpfile_jmap.hprof 20886

 

 

 

 

接着就是可以用MAT工具,或者是Eclipse MAT的內存分析插件,來對hprof文件進行分析,看看到底是哪個王八蛋對象太多了,導致內存溢出了

 

 

 

 

或者使用jdk的目錄下的bin目錄下的:jvisualvm.exe

 

 

 

8、總結:

一般常見的OOM,要么是短時間內涌入大量的對象,導致你的系統根本支持不住,此時你可以考慮優化代碼,或者是加機器;要么是長時間來看,你的很多對象不用了但是還被引用,就是內存泄露了,你也是優化代碼就好了;這就會導致大量的對象不斷進入老年代,然后頻繁full gc之后始終沒法回收,就撐爆了

 

要么是加載的類過多,導致class在永久代理保存的過多,始終無法釋放,就會撐爆

 

我這里可以給大家最后提一點,人家肯定會問你有沒有處理過線上的問題,你就說有,最簡單的,你說有個小伙子用了本地緩存,就放map里,結果沒控制map大小,可以無限擴容,最終導致內存爆了,后來解決方案就是用了一個ehcache框架,自動LRU清理掉舊數據,控制內存占用就好了。

 

另外,務必提到,線上jvm必須配置-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/path/heap/dump。因為這樣就是說OOM的時候自動導出一份內存快照,你就可以分析發生OOM時的內存快照了,到底是哪里出現的問題

 

9、修改代碼調優,修改jvm配置調優,部署接口壓測

代碼進行優化、根據壓測的情況去進行一定的jvm參數的調優,一個系統的QPS,一個是系統的接口的性能,壓測到一定程度的時候 ,機器的cpu、內存、io、磁盤的一些負載情況,jvm的表現

 

10、流程

 

附加:系統頻繁full gc

OOM稍微好點的是頻繁full gc,如果OOM就是系統自動就掛了,很慘,你絕對是超級大case,但是頻繁full gc會好多,其實就是表現為經常請求系統的時候,很卡,一個請求卡半天沒響應,就是會覺得系統性能很差。

 

首先,你必須先加上一些jvm的參數,讓線上系統定期打出來gc的日志:

 

-XX:+PrintGCTimeStamps

-XX:+PrintGCDeatils

-Xloggc:<filename>

 

這樣如果發現線上系統經常卡頓,可以立即去查看gc日志,大概長成這樣:

 

 

如果要是發現每次Full GC過后,ParOldGen就是老年代老是下不去,那就是大量的內存一直占據着老年代,啥事兒不干,回收不掉,所以頻繁的full gc,每次full gc肯定會導致一定的stop the world卡頓,這是不可能完全避免的

 

接着采用跟之前一樣的方法,就是dump出來一份內存快照,然后用Eclipse MAT插件分析一下好了,看看哪個對象量太大了

 

接着其實就是跟具體的業務場景相關了,要看具體是怎么回事,常見的其實要么是內存泄露,要么就是類加載過多導致永久代快滿了,此時一般就是針對代碼邏輯來優化一下。

 

給大家還是舉個例子吧,我們線上系統的一個真實例子,大家可以用這個例子在面試里來說,比如說當時我們有個系統,在后台運行,每次都會一下子從mysql里加載幾十萬行數據進來各種處理,類似於定時批量處理,這個時候,如果對幾十萬數據的處理比較慢,就會導致比如幾分鍾里面,大量數據囤積在老年代,然后沒法回收,就會頻繁full gc。

 

當時我們其實就是根據這個發現了當時兩台機器已經不夠了,因為我們當時線上用了兩台4核8G的虛擬機在跑,明顯不夠了,就要加機器了,所以增加了機器,每台機器處理更少的數據量,那不就ok了,馬上就緩解了頻繁full gc的問題了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM