web-dev-qa-db-ja.com

大きなサイズの配列を作成できないのはなぜですか?

最大intサイズの配列を作成できないのはなぜですか?

int i = 2147483647;
int[] array = new int[i];

私はこの説明を見つけました:

Java配列は32ビットintを介してアクセスされるため、理論上の最大配列サイズは2147483647要素になります。

しかし、ご覧のとおり、私のコードは機能しません。サイズのある配列を作成することもできません

new int[Integer.MAX_VALUE - 5];

技術的な詳細

  • 64ビットHotSpotJVM
  • OSX 10.10.4

[〜#〜] ps [〜#〜]

なぜ -5実際に?

7
JohnWinter

理論

考えられる例外は2つあります。

  • OutOfMemoryError: Java heap spaceは、配列がJavaヒープスペースに収まらないことを意味します。解決するには、JVMオプション-Xmxを使用して最大ヒープサイズを増やすことができます。また、考慮してください。その オブジェクトの最大サイズは最大ヒープ生成より大きくすることはできません
  • OutOfMemoryError: Requested array size exceeds VM limitは、プラットフォーム固有のサイズを超えたことを意味します:
    • 上限は、配列内のインデックスを記述するために使用されるサイズタイプの制限によって設定されるため、理論上の配列サイズは2^31-1=2147483647要素によって制限されます。
    • もう1つの制限は、JVM /プラットフォーム固有です。 第10章:Java言語仕様、Java SE 7 Edition の配列)によると、配列の長さに厳密な制限はありません。したがって、JLSに違反することなくアレイサイズを縮小できます。

練習

HotSpotのJVM配列サイズは、内部表現によって制限されます。 GCコードでは、JVMはヒープワードの配列のサイズをintとして渡し、ヒープワードからjintに変換し直すと、オーバーフローが発生する可能性があります。したがって、クラッシュや予期しない動作を回避するために、最大配列長は (max size --header size) によって制限されます。 ヘッダーサイズ は、実行しているJVMのビルドに使用されたC/C++コンパイラ(Linuxの場合はgcc、macosの場合はclang)、およびランタイム設定(UseCompressedClassPointersなど)によって異なります。たとえば私のLinuxでは:

  • Java HotSpot(TM)64ビットサーバーVM 1.6.0_45制限Integer.MAX_VALUE
  • Java HotSpot(TM)64ビットサーバーVM 1.7.0_72制限Integer.MAX_VALUE-1
  • Java HotSpot(TM)64ビットサーバーVM 1.8.0_40制限Integer.MAX_VALUE-2

便利なリンク

20
Ivan Mamontov

一部のVMは、配列内のいくつかのヘッダーワードを予約します。

「安全な」最大数はbe 2 147 483 639 (Integer.MAX_VALUE - 8)です。

ソース-http://www.docjar.com/html/api/Java/util/ArrayList.Java.html

**
  191        * The maximum size of array to allocate.
  192        * Some VMs reserve some header words in an array.
  193        * Attempts to allocate larger arrays may result in
  194        * OutOfMemoryError: Requested array size exceeds VM limit
  195        */
  196       private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

したがって、システム上のJVMで使用可能な最大メモリに依存します。

編集:なぜそれがOOMを表示しているのか。

要素数= 2 147 483 639

1つの要素に必要なバイト数= 4

要素だけの合計メモリ8589934556 KB == 8.589934555999999 GB

配列の合計メモリ使用量が8バイトの倍数でない場合、サイズは次の8の倍数に切り上げられます。

したがって、オーバーヘッドのために割り当てているものよりも多くのものが必要であり、それは継続的なメモリである必要があります

1
Ankur Anand

その割り当てに十分なヒープを用意するだけでは十分ではありません。十分なサイズの単一のヒープregionが必要です。ご存知のように、ヒープは世代に分けられます。

8 GBの単一の割り当ての場合、単一のヒープ領域(およびある程度のオーバーヘッド)に対してその量を確保する必要があります。 12GBの-Xmxあなたはまだ不足しているかもしれません。追加のオプションを使用して、旧世代のサイズを制御します。

1
Marko Topolnik

さて、Ivanは、配列の長さには明確に定義された上限があり、JVM /プラットフォームに依存していることをすでに正しく指摘しています。実際、さらに重要なことに、コードで実際に作成できる配列の長さは、主に実行中にプログラムに割り当てた最大ヒープスペースの量によって制御されると彼は述べました。

彼の説明をサポートするために、小さなコードスニペットを追加したいと思います。たとえば、理論的には、配列[]は長さ<= INTEGER.MAX_VALUE-x(ここで、xはJVM /プラットフォーム固有のヘッダーサイズ)を受け入れる必要がありますが、実行するとします以下 JavaプログラムとVMオプション-Xmx32mの場合、作成された配列はいずれもMAX_ARRAY_SIZEに近い長さに達していないことがわかります(つまり、2147483639)。

byte []配列:1バイト
0 l = 1048576 s = 1mb
1 l = 2097152 s = 2mb
2 l = 4194304 s = 4mb
3 l = 8388608 s = 8mb
Java.lang.OutOfMemoryError:Javaヒープスペースl = 16777216 s = 16mb

char []配列:2バイト
0 l = 1048576 s = 2mb
1 l = 2097152 s = 4mb
2 l = 4194304 s = 8mb
Java.lang.OutOfMemoryError:Javaヒープスペースl = 8388608 s = 16mb

int []配列:4バイト
0 l = 1048576 s = 4mb
1 l = 2097152 s = 8mb
Java.lang.OutOfMemoryError:Javaヒープスペースl = 4194304 s = 16mb

double []配列:8バイト
0 l = 1048576 s = 8mb
Java.lang.OutOfMemoryError:Javaヒープスペースl = 2097152 s = 16mb

以下のコードは次のとおりです:

    byte[] barray = null;
    System.out.println("\nbyte[] array : 1 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            barray = new byte[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + barray.length + " s=" + barray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        barray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + (int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    char[] carray = null;
    System.out.println("\nchar[] array : 2 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            carray = new char[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + carray.length + " s=" + 2*carray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        carray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 2*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    int[] iarray = null;
    System.out.println("\nint[] array : 4 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            iarray = new int[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + iarray.length + " s=" + 4*iarray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        iarray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 4*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    double[] darray = null;
    System.out.println("\ndouble[] array : 8 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            darray = new double[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + darray.length + " s=" + 8*darray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        darray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 8*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }
0
sactiw

cmdに移動してこの行を入力し、最大ヒープサイズを見つけます

javaw -XX:+PrintFlagsFinal | find "MaxHeapSize"

次に、それを1.5で割ると、コンピューターの配列のおおよその最大サイズが得られます。

0
Anonymous