7.1 要求されたアレイ サイズが VM 制限を超えています 概要
Java には、アプリケーションが割り当てることができる最大配列サイズに制限があり、正確な制限はプラットフォームによって異なりますが、通常は 10 ~ 21 億要素の間になります。
これに直面した場合java.lang.OutOfMemoryError: Requested array size exceeds VM limit
、それは、JVM がサポートできるより大きな配列を割り当てようとしたためにアプリケーションがクラッシュしたことを意味します。
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 ビット 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();
}
}
この例では 4 回ループし、各ループでプリミティブの長い配列を初期化します。プログラムが初期化しようとしている配列のサイズは、反復ごとに 1 ずつ増加し、最終的には 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)
最後の 2 回の試行が行われる前にjava.lang.OutOfMemoryError: Requested array size exceeds VM limit
、割り当て失敗により、より一般的なjava.lang.OutOfMemoryError: Java heap space
メッセージが報告されることに注意してください。これは、割り当てようとしている 2^31-1 int には 8G メモリが必要であり、これは JVM のデフォルト メモリよりもはるかに大きいためです。
この例は、このエラーがいかにまれであるかを示しています。VM が配列サイズの制限に達していることを確認するには、プラットフォームの制限と、要件を満たす 2 つの配列の長さの間のデータを割り当てる必要がInteger.MAX_INT
ありInteger.MAX_INT-1
ますInteger.MAX_INT
。
7.4 解決策
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
次の 2 つの状況で発生します。
- 配列が大きすぎて、最終的なサイズがプラットフォームの制限
Integer.MAX_INT
と範囲の間にあります。 - 具体的には、その制限を実験するために 2^31-1 要素より大きい配列を割り当てようとしています。
最初のケースでは、コードベースをチェックして、そのような大きな配列が本当に必要かどうかを確認してください。おそらく、配列のサイズを小さくしてそれで済むかもしれません。または、配列をより小さな散布点に分割し、プラットフォームの制限内で必要なデータをバッチにロードします。
2 番目のケースでは、Java 配列は int によってインデックス付けされることに注意してください。そのため、プラットフォームで標準のデータ構造を使用する場合、要素の配列は 2^31-1 を超えることはできません。実際、この場合、コンパイラはコンパイル中に「エラー: 整数が大きすぎます」と通知したときにすでに停止しています。
ただし、非常に大規模なデータセットを扱う場合は、オプションを再検討する必要があります。必要なデータを小さいバッチでロードしながら、標準の Java ツールを使用することもできます。そうでない場合は、ユーティリティを使用しなくなる可能性があります。これを実現する 1 つの方法は、次のとおりです。sun.misc.Unsafe
授業。これにより、C と同様にメモリを直接割り当てることができます。