常見OutOfMemoryError 分析與解決

java.lang.OutOfMemoryError:Javaheap space 

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

原因分析:

Javaheap space 錯誤產生的常見原因可以分爲以下幾類:

1、請求創建一個超大對象,通常是一個大數組。

2、超出預期的訪問量/數據量,通常是上游系統請求流量飆升,常見於各類促銷/秒殺活動,可以結合業務流量指標排查是否有尖狀峯值。

3、過度使用終結器(Finalizer),該對象沒有立即被 GC。

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

解決:

針對大部分情況,通常只需要通過 -Xmx 參數調高 JVM 堆內存空間即可。如果仍然沒有解決,可以參考以下情況做進一步處理:

1、如果是超大對象,可以檢查其合理性,比如是否一次性查詢了數據庫全部結果,而沒有做結果數限制。

2、如果是業務峯值壓力,可以考慮添加機器資源,或者做限流降級。

3、如果是內存泄漏,需要找到持有的對象,修改代碼設計,比如關閉沒有釋放的連接。

 

java.lang.OutOfMemoryError:GC overhead limit exceeded

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

此類問題的原因與解決方案跟 Javaheap space 非常類似,可以參考上文

java.lang.OutOfMemoryError:PermGen space

該錯誤表示永久代(Permanent Generation)已用滿,通常是因爲加載的 class 數目太多或體積太大。

原因分析

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

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

2、常量池;

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

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

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

解決方案

根據 Permgen space 報錯的時機,可以採用不同的解決方案,如下所示:

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

2、應用重新部署時報錯,很可能是沒有應用沒有重啓,導致加載了多份 class 信息,只需重啓 JVM 即可解決。

3、運行時報錯,應用程序可能會動態創建大量 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。

java.lang.OutOfMemoryError:Metaspace

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

此類問題的原因與解決方法跟 Permgenspace 非常類似,可以參考上文。需要特別注意的是調整 Metaspace 空間大小的啓動參數爲 -XX:MaxMetaspaceSize

java.lang.OutOfMemoryError:Unable to create new native thread

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

原因分析

JVM 向 OS 請求創建 native 線程失敗,就會拋出 Unableto createnewnativethread,常見的原因包括以下幾類:

1、線程數超過操作系統最大線程數 ulimit 限制;

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

3、native 內存不足;

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

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

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

3、操作系統嘗試創建一個新的 native 線程,併爲其分配內存;

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

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

解決方案

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

2、降低 Java Heap Space 大小;

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

4、限制線程池大小;

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

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

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


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章