java.lang.OutOfMemoryError 解決方法


java.lang.OutOfMemoryError錯誤:

1. Java heap space

2. GC overhead limit exceeded

3. GC overhead limit exceeded

4. Metaspace

5. Unable to create new native thread

6. Out of swap space

7. Requested array size exceeds VM limit

8. nativeGetNewTLA

1. Java heap space

原因分析

heap和permgen的最大內存大小, 由JVM啟動參數 -Xmx-XX:MaxPermSize 指定. 如果沒有明確指定, 則根據平台類型(OS版本+ JVM版本)和物理內存的大小來確定。

假如在創建新的對象時, 堆內存中的空間不足以存放新創建的對象, 就會引發java.lang.OutOfMemoryError: Java heap space 錯誤。

不管機器上還沒有空閑的物理內存, 只要堆內存使用量達到最大內存限制,就會拋出 java.lang.OutOfMemoryError: Java heap space 錯誤。

產生 java.lang.OutOfMemoryError: Java heap space 錯誤的原因, 很多時候, 就類似於將 XXL 號的對象,往 S 號的 Java heap space 里面塞。其實清楚了原因, 就很容易解決對不對? 只要增加堆內存的大小, 程序就能正常運行. 另外還有一些比較復雜的情況, 主要是由代碼問題導致的:

 

  • 超出預期的訪問量/數據量。 應用系統設計時,一般是有 “容量” 定義的, 部署這么多機器, 用來處理一定量的數據/業務。 如果訪問量突然飆升, 超過預期的閾值, 類似於時間坐標系中針尖形狀的圖譜, 那么在峰值所在的時間段, 程序很可能就會卡死、並觸發 java.lang.OutOfMemoryError: Java heap space 錯誤。

 

 

  • 內存泄露(Memory leak). 這也是一種經常出現的情形。由於代碼中的某些錯誤, 導致系統占用的內存越來越多. 如果某個方法/某段代碼存在內存泄漏的, 每執行一次, 就會(有更多的垃圾對象)占用更多的內存. 隨着運行時間的推移, 泄漏的對象耗光了堆中的所有內存, 那么 java.lang.OutOfMemoryError: Java heap space 錯誤就爆發了。

 

解決方案

如果設置的最大內存不滿足程序的正常運行, 只需要增大堆內存即可,   -Xmx1024m

2. GC overhead limit exceeded

原因分析

JVM拋出 java.lang.OutOfMemoryError: GC overhead limit exceeded 錯誤就是發出了這樣的信號: 執行垃圾收集的時間比例太大, 有效的運算量太小. 默認情況下, 如果GC花費的時間超過 98%, 並且GC回收的內存少於 2%, JVM就會拋出這個錯誤。

注意, java.lang.OutOfMemoryError: GC overhead limit exceeded 錯誤只在連續多次 GC 都只回收了不到2%的極端情況下才會拋出。假如不拋出 GC overhead limit 錯誤會發生什么情況呢? 那就是GC清理的這么點內存很快會再次填滿, 迫使GC再次執行. 這樣就形成惡性循環, CPU使用率一直是100%, 而GC卻沒有任何成果. 系統用戶就會看到系統卡死 - 以前只需要幾毫秒的操作, 現在需要好幾分鍾才能完成。

解決方案

有一種應付了事的解決方案, 就是不想拋出 “java.lang.OutOfMemoryError: GC overhead limit exceeded” 錯誤信息, 則添加下面啟動參數: -XX:-UseGCOverheadLimit

3. GC overhead limit exceeded

原因分析

在JDK1.7及之前的版本, 永久代(permanent generation) 主要用於存儲加載/緩存到內存中的 class 定義, 包括 class 的 名稱(name), 字段(fields), 方法(methods)和字節碼(method bytecode); 以及常量池(constant pool information); 對象數組(object arrays)/類型數組(type arrays)所關聯的 class, 還有 JIT 編譯器優化后的class信息等。

很容易看出, PermGen 的使用量和JVM加載到內存中的 class 數量/大小有關。可以說 java.lang.OutOfMemoryError: PermGen space 的主要原因, 是加載到內存中的 class 數量太多或體積太大。

解決方案

1. 解決程序啟動時產生的 OutOfMemoryError

在程序啟動時, 如果 PermGen 耗盡而產生 OutOfMemoryError 錯誤, 那很容易解決. 增加 PermGen 的大小, 讓程序擁有更多的內存來加載 class 即可. 修改 -XX:MaxPermSize 啟動參數, 類似下面這樣: java -XX:MaxPermSize=512m com.yourcompany.YourClass

2. 解決 redeploy 時產生的 OutOfMemoryError

我們可以進行堆轉儲分析(heap dump analysis) —— 在 redeploy 之后, 執行堆轉儲, 類似下面這樣: jmap -dump:format=b,file=dump.hprof <process-id>

3. 解決運行時產生的 OutOfMemoryError

如果在運行的過程中發生 OutOfMemoryError, 首先需要確認 GC是否能從PermGen中卸載class。 官方的JVM在這方面是相當的保守(在加載class之后,就一直讓其駐留在內存中,即使這個類不再被使用). 但是, 現代的應用程序在運行過程中, 會動態創建大量的class, 而這些class的生命周期基本上都很短暫, 舊版本的JVM 不能很好地處理這些問題。那么我們就需要允許JVM卸載class。使用下面的啟動參數: -XX:+CMSClassUnloadingEnabled

4. Metaspace

原因分析

從Java 8開始,內存結構發生重大改變, 不再使用Permgen, 而是引入一個新的空間: Metaspace. 這種改變基於多方面的考慮, 部分原因列舉如下:

 

 

  • 為了 GC 性能 的提升, 使得垃圾收集過程中的並發階段不再 停頓, 另外對 metadata 進行特定的遍歷(specific iterators)。
  • G1垃圾收集器 的並發 class unloading 進行深度優化。

在Java8中,將之前 PermGen 中的所有內容, 都移到了 Metaspace 空間。例如: class 名稱, 字段, 方法, 字節碼, 常量池, JIT優化代碼, 等等。

Metaspace 的使用量與JVM加載到內存中的 class 數量/大小有關。可以說, java.lang.OutOfMemoryError: Metaspace 錯誤的主要原因, 是加載到內存中的 class 數量太多或者體積太大

解決方案

如果拋出與 Metaspace 有關的 OutOfMemoryError , 第一解決方案是增加 Metaspace 的大小. 使用下面這樣的啟動參數: -XX:MaxMetaspaceSize=512m

5. Unable to create new native thread

原因分析

JVM向操作系統申請創建新的 native thread(原生線程)時, 就有可能會碰到 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤. 如果底層操作系統創建新的 native thread 失敗, JVM就會拋出相應的OutOfMemoryError. 原生線程的數量受到具體環境的限制, 通過一些測試用例可以找出這些限制, 請參考下文的示例. 但總體來說, 導致 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤的場景大多經歷以下這些階段:

 

  • Java程序向JVM請求創建一個新的Java線程;

 

  • JVM本地代碼(native code)代理該請求, 嘗試創建一個操作系統級別的 native thread(原生線程);
  • 操作系統嘗試創建一個新的native thread, 需要同時分配一些內存給該線程;
  • 如果操作系統的虛擬內存已耗盡, 或者是受到32位進程的地址空間限制(約2-4GB), OS就會拒絕本地內存分配;
  • JVM拋出 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤。

解決方案

有時可以修改系統限制來避開 Unable to create new native thread 問題. 假如JVM受到用戶空間(user space)文件數量的限制, 像下面這樣,就應該想辦法增大這個值:

[root@dev ~]# ulimit -a
    core file size          (blocks, -c) 0
    ...... 省略部分內容 ......
    max user processes              (-u) 1800

6. Out of swap space

原因分析

如果 native heap 內存耗盡, 內存分配時, JVM 就會拋出 java.lang.OutOfmemoryError: Out of swap space? 錯誤消息, 這個消息告訴用戶, 請求分配內存的操作失敗了。

Java進程使用了虛擬內存才會發生這個錯誤。 對 Java的垃圾收集 來說這是很難應付的場景。即使現代的 GC算法 很先進, 但虛擬內存交換引發的系統延遲, 會讓 GC暫停時間 膨脹到令人難以容忍的地步。

通常是操作系統層面的原因導致 java.lang.OutOfMemoryError: Out of swap space? 問題, 例如:

  • 操作系統的交換空間太小。
  • 機器上的某個進程耗光了所有的內存資源。

當然也可能是應用程序的本地內存泄漏(native leak)引起的, 例如, 某個程序/庫不斷地申請本地內存,卻不進行釋放。

解決方案

第一種, 也是最簡單的方法, 增加虛擬內存(swap space) 的大小. 各操作系統的設置方法不太一樣, 比如Linux,可以使用下面的命令設置:

swapoff -a dd if=/dev/zero of=swapfile bs=1024 count=655360 mkswap swapfile swapon swapfile

7. Requested array size exceeds VM limit

原因分析

這個錯誤是由JVM中的本地代碼拋出的. 在真正為數組分配內存之前, JVM會執行一項檢查: 要分配的數據結構在該平台是否可以尋址(addressable). 當然, 這個錯誤比你所想的還要少見得多。

一般很少看到這個錯誤, 因為Java使用 int 類型作為數組的下標(index, 索引)。在Java中, int類型的最大值為 2^31 – 1 = 2,147,483,647。大多數平台的限制都約等於這個值 —— 例如在 64位的 MB Pro 上, Java 1.7 平台可以分配長度為 2,147,483,645, 以及 Integer.MAX_VALUE-2) 的數組。

再增加一點點長度, 變成 Integer.MAX_VALUE-1 時, 就會拋出我們所熟知的 OutOfMemoryErrorException in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit`

在有的平台上, 這個最大限制可能還會更小一些, 例如在32位Linux, OpenJDK 6 上面, 數組長度大約在 11億左右(約2^30) 就會拋出 “java.lang.OutOfMemoryError: Requested array size exceeds VM limit“ 錯誤。要找出具體的限制值, 

解決方案

發生 java.lang.OutOfMemoryError: Requested array size exceeds VM limit 錯誤的原因可能是:

  • 數組太大, 最終長度超過平台限制值, 但小於 Integer.MAX_INT
  • 為了測試系統限制, 故意分配長度大於 2^31-1 的數組。

第一種情況, 需要檢查業務代碼, 確認是否真的需要那么大的數組。如果可以減小數組長度, 那就萬事大吉. 如果不行,可能需要把數據拆分為多個塊, 然后根據需要按批次加載。

如果是第二種情況, 請記住, Java 數組用 int 值作為索引。所以數組元素不能超過 2^31-1 個. 實際上, 代碼在編譯階段就會報錯,提示信息為 “error: integer number too large”。

8. nativeGetNewTLA

weblogic服務器內存溢出,經常報 java.lang.OutOfMemoryError: nativeGetNewTLA。 經查是由於 weblogic 使用 jrockit jvm時才會出現這樣的問題。網上查了好久解決方案,給出的都是 調整 -XXtlaSize 這個參數。按照網上的說法,我將 -XXtlaSize 調成512M依然出錯。
郁悶之余,直接改weblogic的jvm, 改完之后就沒有再報錯了。
修改方法:在startWeblgoic.sh 頭上加一句 JAVA_VENDOR=Sun 即可。

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM