7. Requested array size exceeds VM limit

7.1 Requested array size exceeds VM limit 概述

Java對你的應用可以分配的最大數組大小有限制. 準確的限制與平臺有關, 但是通常都位於10到21億元素之間.

當你面臨java.lang.OutOfMemoryError: Requested array size exceeds VM limit, 意味着應用因爲嘗試分配一個大於JVM可以支持的數組而報錯crash.

7.2 原因

該錯誤是由JVM的本地代碼拋出的. 它發生在爲一個數組分配內存之前, 這時JVM會執行一個與平臺有關的檢查: 是否待分配的數據結構在這個平臺是可尋址的. 這個錯誤並不像你最初想象的那樣普遍。

你很少面對這個錯誤的原因是 Java 數組是由 int類型索引的. 在Java中最大的正整數是: 2^31 -1 = 2,147,483,647. 平臺相關的限制確實相當接近這個數字 – 例如在MacBook Pro, Java 1.7, 可以初始化數組到2,147,483,645 或 Integer.MAX_VALUE-2 元素.

數組長度再加1到Integer.MAX_VALUE-1 會導致該OutOfMemoryError:

Exception in thread “main” java.lang.OutOfMemoryError:
Requested array size exceeds VM limit

但是這個限制有時也並不是那麼高 – 在32-bit Linux, OpenJDK 6上, 你會在分配一個大約11億元素的數組時候出現java.lang.OutOfMemoryError: Requested array size exceeds VM limit報錯. 要知道你的特定環境的限制大小, 可以運行下面這個小的測試程序.

7.3 示例

要嘗試重現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();
    }
}

該示例循環四次, 並在每次循環中初始化一個長基元數組。該程序試圖初始化的數組大小在每次迭代時都增長一個, 最終達到整數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)

注意到在最後2次嘗試面臨java.lang.OutOfMemoryError: Requested array size exceeds VM limit之前, 分配失敗報出了更爲常見的java.lang.OutOfMemoryError: Java heap space消息. 這是因爲你嘗試分配空間的 2^31-1 int 需要8G內存, 遠大於JVM的默認內存.

這個例子也說明了這個錯誤很稀少 – 爲了看到VM達到數組大小的限制, 你需要分配一個數據剛好位於平臺的限制和Integer.MAX_INT之間. 當這個例子在64bit Mac OS X, Hotspot 7上運行時, 只有2個滿足要求的數組長度: Integer.MAX_INT-1 and Integer.MAX_INT.

7.4 解決方案

java.lang.OutOfMemoryError: Requested array size exceeds VM limit在以下2種情況下會出現:

  • 你的數組增長的太大, 最終大小位於平臺的限制和Integer.MAX_INT之間
  • 您特意嘗試分配大於 2 ^ 31-1 元素的數組來試驗該限制。

在第一種情況下, 檢查您的代碼庫, 看看您是否真的需要這麼大的數組。也許你可以減少數組的大小, 並完成它。或將陣列劃分爲較小的散點, 並將所需的數據加載到您的平臺限制中的批次中。

在第二種情況下-請記住, Java 數組是由 int 索引. 因此,在使用平臺中的標準數據結構時, 您不能超過 2 ^ 31-1 元素的數組。實際上, 在這種情況下, 編譯器在編譯過程中宣佈 “錯誤: 整數太大(error: integer number too large)” 時已經阻止了您.

但是, 如果你真的使用真正的大型數據集, 你需要重新考慮你的選擇. 您可以在較小的批次中加載所需的數據, 並且仍然使用標準的 Java 工具, 或者您可能會超出標準的實用程序. 實現這一目標的一個方法是查看 sun.misc.Unsafe 類。這使您可以像在 C 中一樣直接分配內存。

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