OOM 常見緣由及解決方案

當 JVM 內存嚴重不足時,就會拋出 java.lang.OutOfMemoryError 錯誤。本文總結了常見的 OOM 緣由及其解決方法,以下圖所示。若有遺漏或錯誤,歡迎補充指正。java

一、Java heap space
數據庫

當堆內存(Heap Space)沒有足夠空間存放新建立的對象時,就會拋出 java.lang.OutOfMemoryError:Javaheap space 錯誤(根據實際生產經驗,能夠對程序日誌中的 OutOfMemoryError 配置關鍵字告警,一經發現,當即處理)。數組

緣由分析

Javaheap space 錯誤產生的常見緣由能夠分爲如下幾類:緩存

一、請求建立一個超大對象,一般是一個大數組。eclipse

二、超出預期的訪問量/數據量,一般是上游系統請求流量飆升,常見於各種促銷/秒殺活動,能夠結合業務流量指標排查是否有尖狀峯值。優化

三、過分使用終結器(Finalizer),該對象沒有當即被 GC。spa

四、內存泄漏(Memory Leak),大量對象引用沒有釋放,JVM 沒法對其自動回收,常見於使用了 File 等資源沒有回收。操作系統

解決方案

針對大部分狀況,一般只須要經過 -Xmx 參數調高 JVM 堆內存空間便可。若是仍然沒有解決,能夠參考如下狀況作進一步處理:線程

一、若是是超大對象,能夠檢查其合理性,好比是否一次性查詢了數據庫所有結果,而沒有作結果數限制。設計

二、若是是業務峯值壓力,能夠考慮添加機器資源,或者作限流降級。

三、若是是內存泄漏,須要找到持有的對象,修改代碼設計,好比關閉沒有釋放的鏈接。

二、GC overhead limit exceeded

當 Java 進程花費 98% 以上的時間執行 GC,但只恢復了不到 2% 的內存,且該動做連續重複了 5 次,就會拋出 java.lang.OutOfMemoryError:GC overhead limit exceeded 錯誤。簡單地說,就是應用程序已經基本耗盡了全部可用內存, GC 也沒法回收。

此類問題的緣由與解決方案跟 Javaheap space 很是相似,能夠參考上文。

三、Permgen space

該錯誤表示永久代(Permanent Generation)已用滿,一般是由於加載的 class 數目太多或體積太大。

緣由分析

永久代存儲對象主要包括如下幾類:

一、加載/緩存到內存中的 class 定義,包括類的名稱,字段,方法和字節碼;

二、常量池;

三、對象數組/類型數組所關聯的 class;

四、JIT 編譯器優化後的 class 信息。

PermGen 的使用量與加載到內存的 class 的數量/大小正相關。

解決方案

根據 Permgen space 報錯的時機,能夠採用不一樣的解決方案,以下所示:

一、程序啓動報錯,修改 -XX:MaxPermSize 啓動參數,調大永久代空間。

二、應用從新部署時報錯,極可能是沒有應用沒有重啓,致使加載了多份 class 信息,只需重啓 JVM 便可解決。

三、運行時報錯,應用程序可能會動態建立大量 class,而這些 class 的生命週期很短暫,可是 JVM 默認不會卸載 class,能夠設置 -XX:+CMSClassUnloadingEnabled-XX:+UseConcMarkSweepGC這兩個參數容許 JVM 卸載 class。

若是上述方法沒法解決,能夠經過 jmap 命令 dump 內存對象 jmap-dump:format=b,file=dump.hprof<process-id> ,而後利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析開銷最大的 classloader 和重複 class。

四、Metaspace

JDK 1.8 使用 Metaspace 替換了永久代(Permanent Generation),該錯誤表示 Metaspace 已被用滿,一般是由於加載的 class 數目太多或體積太大。

此類問題的緣由與解決方法跟 Permgenspace 很是相似,能夠參考上文。須要特別注意的是調整 Metaspace 空間大小的啓動參數爲 -XX:MaxMetaspaceSize

五、Unable to create new native thread

每一個 Java 線程都須要佔用必定的內存空間,當 JVM 向底層操做系統請求建立一個新的 native 線程時,若是沒有足夠的資源分配就會報此類錯誤。

緣由分析

JVM 向 OS 請求建立 native 線程失敗,就會拋出 Unableto createnewnativethread,常見的緣由包括如下幾類:

一、線程數超過操做系統最大線程數 ulimit 限制;

二、線程數超過 kernel.pid_max(只能重啓);

三、native 內存不足;

該問題發生的常見過程主要包括如下幾步:

一、JVM 內部的應用程序請求建立一個新的 Java 線程;

二、JVM native 方法代理了該次請求,並向操做系統請求建立一個 native 線程;

三、操做系統嘗試建立一個新的 native 線程,併爲其分配內存;

四、若是操做系統的虛擬內存已耗盡,或是受到 32 位進程的地址空間限制,操做系統就會拒絕本次 native 內存分配;

五、JVM 將拋出 java.lang.OutOfMemoryError:Unableto createnewnativethread 錯誤。

解決方案

一、升級配置,爲機器提供更多的內存;

二、下降 Java Heap Space 大小;

三、修復應用程序的線程泄漏問題;

四、限制線程池大小;

五、使用 -Xss 參數減小線程棧的大小;

六、調高 OS 層面的線程最大數:執行 ulimia-a 查看最大線程數限制,使用 ulimit-u xxx 調整最大線程數限制。

ulimit -a .... 省略部份內容 ..... max user processes (-u) 16384

你還有遇到哪些問題呢?你遇到問題的解決方案是哪些呢?歡迎留言指出一塊兒交流。

相關文章
相關標籤/搜索