JVM-OOM排查思路

JVM 內(nèi)存溢出排查思路:

一般來講,我們會先用 free 命令先來檢查一發(fā)內(nèi)存的各種情況,

堆內(nèi)內(nèi)存
JVM 中的內(nèi)存不足,OOM 大致可以分為以下幾種:

① Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
這個意思是沒有足夠的內(nèi)存空間給線程分配 Java 棧,基本上還是線程池代碼寫的有問題,比如說忘記 shutdown,所以說應(yīng)該首先從代碼層面來尋找問題,使用 jstack 或者 jmap。
如果一切都正常,JVM 方面可以通過指定 Xss 來減少單個 thread stack 的大小。另外也可以在系統(tǒng)層面,可以通過修改 /etc/security/limits.confnofile 和 nproc 來增大 os 對線程的限制。

② Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
這個意思是堆的內(nèi)存占用已經(jīng)達(dá)到 -Xmx 設(shè)置的最大值,應(yīng)該是最常見的的 OOM 錯誤了。
解決思路仍然是先應(yīng)該在代碼中找,懷疑存在內(nèi)存泄漏,通過 jstack 和 jmap 去定位問題。如果說一切都正常,才需要通過調(diào)整 Xmx 的值來擴(kuò)大內(nèi)存。

③ Caused by: java.lang.OutOfMemoryError: Meta space
這個意思是元數(shù)據(jù)區(qū)的內(nèi)存占用已經(jīng)達(dá)到 XX:MaxMetaspaceSize 設(shè)置的最大值,排查思路和上面的一致,參數(shù)方面可以通過 XX:MaxPermSize 來進(jìn)行調(diào)整

堆外內(nèi)存

首先堆外內(nèi)存溢出表現(xiàn)就是物理常駐內(nèi)存增長快,報錯的話視使用方式都不確定。
如果由于使用 Netty 導(dǎo)致的,那錯誤日志里可能會出現(xiàn) OutOfDirectMemoryError 錯誤,如果直接是 DirectByteBuffer,那會報 OutOfMemoryError: Direct buffer memory。
堆外內(nèi)存溢出往往是和 NIO 的使用相關(guān),一般我們先通過 pmap 來查看下進(jìn)程占用的內(nèi)存情況 pmap -x pid | sort -rn -k3 | head -30,這段意思是查看對應(yīng) pid 倒序前 30 大的內(nèi)存段。這邊可以再一段時間后再跑一次命令看看內(nèi)存增長情況,或者和正常機(jī)器比較可疑的內(nèi)存段在哪里
我們?nèi)绻_定有可疑的內(nèi)存端,需要通過 gdb 來分析 gdb --batch --pid {pid} -ex "dump memory filename.dump {內(nèi)存起始地址} {內(nèi)存起始地址+內(nèi)存塊大小}"。
獲取 dump 文件后可用 heaxdump 進(jìn)行查看 hexdump -C filename | less,不過大多數(shù)看到的都是二進(jìn)制亂碼。NMT 是 Java7U40 引入的 HotSpot 新特性,配合 jcmd 命令我們就可以看到具體內(nèi)存組成了.
需要在啟動參數(shù)中加入 -XX:NativeMemoryTracking=summary 或者 -XX:NativeMemoryTracking=detail,會有略微性能損耗。一般對于堆外內(nèi)存緩慢增長直到爆炸的情況來說,可以先設(shè)一個基線 jcmd pid VM.native_memory baseline。然后等放一段時間后再去看看內(nèi)存增長的情況,通過 jcmd pid VM.native_memory detail.diff(summary.diff) 做一下 summary 或者 detail 級別的 diff, jcmd 分析出來的內(nèi)存十分詳細(xì),包括堆內(nèi)、線程以及 GC(所以上述其他內(nèi)存異常其實都可以用 nmt 來分析),這邊堆外內(nèi)存我們重點關(guān)注 Internal 的內(nèi)存增長,如果增長十分明顯的話那就是有問題了。

GC排查

我們是通過 GC 日志來排查問題的,在啟動參數(shù)中加上 -verbose:gc,-XX:+PrintGCDetails,-XX:+PrintGCDateStamps,-XX:+PrintGCTimeStamps 來開啟 GC 日志。
常見的 Young GC、Full GC 日志含義在此就不做贅述了。針對 GC 日志,我們就能大致推斷出 youngGC 與 Full GC 是否過于頻繁或者耗時過長,從而對癥下藥。

① youngGC 過頻繁
youngGC 頻繁一般是短周期小對象較多,先考慮是不是 Eden 區(qū)/新生代設(shè)置的太小了,看能否通過調(diào)整 -Xmn、-XX:SurvivorRatio 等參數(shù)設(shè)置來解決問題。如果參數(shù)正常,但是 youngGC 頻率還是太高,就需要使用 Jmap 和 MAT 對 dump 文件進(jìn)行進(jìn)一步排查了。

② youngGC 耗時過長
耗時過長問題就要看 GC 日志里耗時耗在哪一塊了。以 G1 日志為例,可以關(guān)注 Root Scanning、Object Copy、Ref Proc 等階段。Ref Proc 耗時長,就要注意引用相關(guān)的對象。Root Scanning 耗時長,就要注意線程數(shù)、跨代引用。Object Copy 則需要關(guān)注對象生存周期。而且耗時分析它需要橫向比較,就是和其他項目或者正常時間段的耗時比較。

③觸發(fā) Full GC
G1 中更多的還是 mixedGC,但 mixedGC 可以和 youngGC 思路一樣去排查。

這是我們排查過程中從不同的角度進(jìn)行思考分析 , 根據(jù)不同的原因進(jìn)行分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容