Java で配列を生成する際に、どこまで大きい要素数を指定できるかは、Java VMの実装によるみたいです。
なので、Oracle の Java VM ではメモリが十分にあっても、次のコードは必ずエラーになります。
int[] max = new int[Integer.MAX_VALUE];
じゃあ、メモリが十分にある場合の上限がどこまでなのかというと、Oracle の Java VM の場合は以下の通りです*1。
32bit の Java VM | 0x3fffffff - 3 (= 1,073,741,820) |
64bit の Java VM | 0x7fffffff - 2 (= 2,147,483,645) |
どういうこと?
まずは、Java 言語仕様を確認したところ、以下の記載がありました。
配列は int 値によってインデックスづけされなければならず、short, byte, char 値も単数の数値格上げによって int 値となるため、インデックス値として使用することができる。
10.4 配列へのアクセス - Java 言語仕様 第3版
(中略)
配列に対するすべてのアクセスは実行時にチェックされ、ゼロより小さい、あるいは配列の長さ以上のインデックスを使用しようとした場合、ArrayIndexOutOfBoundsException がスローされる。
要するに、配列のインデックスには 0 〜 Integer_MAX_VALUE (=0x7fffffff) まで使用できる、とのことです。
どこまで生成できるかについては、別の個所*2に、「(メモリを確保できないなら) OutOfMemoryError がスローされる」という記載があるだけです。
このことから、一見するとメモリが十分にあれば要素数が Integer.MAX_VALUE の配列まで生成できそうな気がしますが、そうではないようです。
実際にやってみると、Oracle の Java VM だと「VMの制限を超えている」というエラーになります。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
メモリ不足であれば「java.lang.OutOfMemoryError: Java heap space」ですが、それとは違うエラーです。
Java VM のコードで確認
Java VMのコードをダウンロードして、上記のメッセージで GREP してみました。
すると見つかったのはこんなコード。
objArrayOop arrayKlass::allocate_arrayArray(int n, int length, TRAPS) { if (length < 0) { THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); } if (length > arrayOopDesc::max_array_length(T_ARRAY)) { report_java_out_of_memory("Requested array size exceeds VM limit"); JvmtiExport::post_array_size_exhausted(); THROW_OOP_0(Universe::out_of_memory_error_array_size()); }
arrayOopDesc::max_array_length 関数が、この OutOfMemoryError の発生条件のようです。
この関数が何かというと…。
// Return the maximum length of an array of BasicType. The length can passed // to typeArrayOop::object_size(scale, length, header_size) without causing an // overflow. We also need to make sure that this will not overflow a size_t on // 32 bit platforms when we convert it to a byte size. static int32_t max_array_length(BasicType type) { assert(type >= 0 && type < T_CONFLICT, "wrong type"); assert(type2aelembytes(type) != 0, "wrong type"); const size_t max_element_words_per_size_t = align_size_down((SIZE_MAX/HeapWordSize - header_size(type)), MinObjAlignment); const size_t max_elements_per_size_t = HeapWordSize * max_element_words_per_size_t / type2aelembytes(type); if ((size_t)max_jint < max_elements_per_size_t) { // It should be ok to return max_jint here, but parts of the code // (CollectedHeap, Klass::oop_oop_iterate(), and more) uses an int for // passing around the size (in words) of an object. So, we need to avoid // overflowing an int when we add the header. See CRs 4718400 and 7110613. return align_size_down(max_jint - header_size(type), MinObjAlignment); } return (int32_t)max_elements_per_size_t; }
よくわかりません!
ただ、HeapWordSize が 32bit と 64bit で違う*3ので、 32bit と 64bit のVMで挙動が違うはず、というのはわかりました。
ということで、冒頭に書いた結果になるようです。