JVM 發生 OOM 的 8 種原因、及解決辦法

目錄

堆內存不足

 原因

解決方法

永久代/元空間溢出

原因 

解決方法

GC overhead limit exceeded

原因

解決方法

方法棧溢出

原因

解決方法

非常規溢出

分配超大數組

swap區溢出

本地方法溢出


擼Java的同學,多多少少都會碰到內存溢出(OOM)的場景,但是造成OOM原因卻不止一個。

堆內存不足

這種場景最爲常見,報錯信息:

java.lang.OutOfMemoryError: Java heap space

 原因

1、代碼中可能存在大對象分配
2、可能存在內存泄露,導致在多次GC之後,還是無法找到一塊足夠大的內存容納當前對象。

解決方法

1、檢查是否存在大對象的分配,最有可能的是大數組分配
2、通過jmap命令,把堆內存dump下來,使用mat工具分析一下,檢查是否存在內存泄露的問題
3、如果沒有找到明顯的內存泄露,使用 -Xmx 加大堆內存
4、還有一點容易被忽略,檢查是否有大量的自定義的 Finalizable 對象,也有可能是框架內部提供的,考慮其存在的必要性

永久代/元空間溢出

java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: Metaspace

原因 

永久代是 HotSot 虛擬機對方法區的具體實現,存放了被虛擬機加載的類信息、常量、靜態變量、JIT編譯後的代碼等。

JDK8後,元空間替換了永久代,元空間使用的是本地內存,還有其它細節變化:

  • 字符串常量由永久代轉移到堆中
  • 和永久代相關的JVM參數已移除

出現永久代或元空間的溢出的原因可能有如下幾種:

1、在Java7之前,頻繁的錯誤使用String.intern方法
2、生成了大量的代理類,導致方法區被撐爆,無法卸載
3、應用長時間運行,沒有重啓

解決方法

永久代/元空間 溢出的原因比較簡單,解決方法有如下幾種:

1、檢查是否永久代空間或者元空間設置的過小
2、檢查代碼中是否存在大量的反射操作
3、dump之後通過mat檢查是否存在大量由於反射生成的代理類
4、放大招,重啓JVM

GC overhead limit exceeded

java.lang.OutOfMemoryError:GC overhead limit exceeded

原因

這個是JDK6新加的錯誤類型,一般都是堆太小導致的。
Sun 官方對此的定義:超過98%的時間用來做GC並且回收了不到2%的堆內存時會拋出此異常。

解決方法

1、檢查項目中是否有大量的死循環或有使用大內存的代碼,優化代碼。
2、添加參數-XX:-UseGCOverheadLimit 禁用這個檢查,其實這個參數解決不了內存問題,只是把錯誤的信息延後,最終出現 java.lang.OutOfMemoryError: Java heap space。
3、dump內存,檢查是否存在內存泄露,如果沒有,加大內存。

方法棧溢出

報錯信息:

java.lang.OutOfMemoryError : unable to create new native Thread

原因

出現這種異常,基本上都是創建的了大量的線程導致的,以前碰到過一次,通過jstack出來一共8000多個線程。

解決方法

1、通過 *-Xss *降低的每個線程棧大小的容量
2、線程總數也受到系統空閒內存和操作系統的限制,檢查是否該系統下有此限制:

  • /proc/sys/kernel/pid_max
  • /proc/sys/kernel/thread-max
  • max_user_process(ulimit -u)
  • /proc/sys/vm/max_map_count

非常規溢出

下面這些OOM異常,可能大部分的同學都沒有碰到過,但還是需要了解一下

分配超大數組

報錯信息 :

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

這種情況一般是由於不合理的數組分配請求導致的,在爲數組分配內存之前,JVM 會執行一項檢查。要分配的數組在該平臺是否可以尋址(addressable),如果不能尋址(addressable)就會拋出這個錯誤。

解決方法就是檢查你的代碼中是否有創建超大數組的地方。

swap區溢出

報錯信息 :

java.lang.OutOfMemoryError: Out of swap space

這種情況一般是操作系統導致的,可能的原因有:

1、swap 分區大小分配不足;
2、其他進程消耗了所有的內存。

解決方案:
1、其它服務進程可以選擇性的拆分出去
2、加大swap分區大小,或者加大機器內存大小

本地方法溢出

報錯信息 :

java.lang.OutOfMemoryError: stack_trace_with_native_method

本地方法在運行時出現了內存分配失敗,和之前的方法棧溢出不同,方法棧溢出發生在 JVM 代碼層面,而本地方法溢出發生在JNI代碼或本地方法處。

這個異常出現的概率極低,就算出現,只能通過操作系統本地工具進行診斷,難度有點大,還是放棄爲妙。

發佈了67 篇原創文章 · 獲贊 10 · 訪問量 6760
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章