1. 程式人生 > 程式設計 >1篇文章搞清楚8種JVM記憶體溢位(OOM)的原因和解決方法

1篇文章搞清楚8種JVM記憶體溢位(OOM)的原因和解決方法

前言

擼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 spacejava.lang.OutOfMemoryError: Metaspace複製程式碼

原因

永久代是 HotSot 虛擬機器器對方法區的具體實現,存放了被虛擬機器器載入的類資訊、常量、靜態變數、JIT編譯後的程式碼等。 JDK8後,元空間替換了永久代,元空間使用的是本地記憶體,還有其它細節變化:
  • 字串常量由永久代轉移到堆中
  • 和永久代相關的JVM引數已移除
可能原因有如下幾種: 1、在Java7之前,頻繁的錯誤使用String.intern()方法 2、執行期間生成了大量的代理類,導致方法區被撐爆,無法解除安裝 3、應用長時間執行,沒有重啟 沒有重啟 JVM 程式一般發生在除錯時,如下面 tomcat 官網的一個 FAQ:
Why does the memory usage increase when I redeploy a web application? That is because your web application has a memory leak. A common issue are “PermGen” memory leaks. They happen because the Classloader (and the Class objects it loaded) cannot be recycled unless some requirements are met (). They are stored in the permanent heap generation by the JVM,and when you redeploy a new class loader is created,which loads another copy of all these classes. This can cause OufOfMemoryErrors eventually. (*) The requirement is that all classes loaded by this classloader should be able to be gc’ed at the same time.

解決方法

因為該OOM原因比較簡單,解決方法有如下幾種: 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
  • maxuserprocess(ulimit -u)
  • /proc/sys/vm/maxmapcount

非常規溢位

下面這些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程式碼或本地方法處。 這個異常出現的概率極低,只能通過作業系統本地工具進行診斷,難度有點大,還是放棄為妙。

最後

歡迎大家關注我的公種浩【程式設計師追風】,文章都會在裡面更新,整理的資料也會放在裡面。