OutOfMemoryError系列(7): Requested array size exceeds VM limit

這是本系列的第七篇文章, 相關文章列表:

Java平臺限制了數組的最大長度。各個版本的具體限制可能稍有不同, 但範圍都在 1 ~ 21億 之間。

outofmemoryerror

如果程序拋出 java.lang.OutOfMemoryError: 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 時, 就會拋出我們所熟知的 OutOfMemoryError:

`Exception 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 錯誤:

 for (int i = 3; i >= 0; i--) {
  try {
    int[] arr = new int[Integer.MAX_VALUE-i];
    System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
  } catch (Throwable t) {
    t.printStackTrace();
  }
}

其中,for循環迭代4次, 每次都去初始化一個 int 數組, 長度從 Integer.MAX_VALUE-3 開始遞增, 到 Integer.MAX_VALUE 爲止. 在 64位 Mac OS X 的 Hotspot 7 平臺上, 執行這段代碼會得到類似下面這樣的結果:

java.lang.OutOfMemoryError: Java heap space
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Java heap space
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)

請注意, 在後兩次迭代拋出 java.lang.OutOfMemoryError: Requested array size exceeds VM limit 錯誤之前, 先拋出了2次 java.lang.OutOfMemoryError: Java heap space 錯誤。 這是因爲 2^31-1 個 int 數佔用的內存超過了JVM默認的8GB堆內存。

此示例也展示了這個錯誤比較罕見的原因 —— 要取得JVM對數組大小的限制, 要分配長度差不多等於 Integer.MAX_INT 的數組. 這個示例運行在64位的Mac OS X, Hotspot 7平臺時, 只有兩個長度會拋出這個錯誤: Integer.MAX_INT-1Integer.MAX_INT

解決方案

發生 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”。

如果確實需要處理超大數據集, 那就要考慮調整解決方案了. 例如拆分成多個小塊,按批次加載; 或者放棄使用標準庫,而是自己處理數據結構,比如使用 sun.misc.Unsafe 類, 通過Unsafe工具類可以像C語言一樣直接分配內存。

原文鏈接: https://plumbr.eu/outofmemoryerror/requested-array-size-exceeds-vm-limit

翻譯日期: 2017年9月21日

翻譯人員: 鐵錨: http://blog.csdn.net/renfufei

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