6

JVM で使用可能なすべてのメモリをほとんど埋める大きな int 配列を作成したいと考えています。たとえば、次のコードを見てください。

    final int numBuffers = (int) ((runtime.freeMemory() - 200000L) / (BUFFER_SIZE));
    System.out.println(runtime.freeMemory());
    System.out.println(numBuffers*(BUFFER_SIZE/4)*4);
    buffers = new int[numBuffers*(BUFFER_SIZE / 4)];

10M のヒープ サイズで実行すると、printlns からの出力が次のようになっているにもかかわらず、OutOfMemoryException がスローされます。

9487176
9273344

配列にはいくらかのオーバーヘッドがあることはわかっていますが、確かに200kではありませんか? java が十分なスペースがあると主張するものにメモリを割り当てられないのはなぜですか? Javaがこれを実行する前に、約4Mに減算される定数を設定する必要があります(その時点で、printlnsは次のようになります: 9487176 5472256 )

さらに当惑することに、バッファを 2D 配列に置き換えると、次のようになります。

buffers = new int[numBuffers][BUFFER_SIZE / 4];

次に、上記の 200k 減算を使用して問題なく実行されます-格納されている整数の量が両方の配列で同じであっても (そして、2D 配列のオーバーヘッドは 1D 配列のオーバーヘッドよりも大きくなりません。格納する他の配列へのそれらの参照)。

何か案は?

4

3 に答える 3

5

VM はヒープ メモリをさまざまな領域 (主にガベージ コレクター用) に分割するため、ほぼヒープ サイズ全体の単一オブジェクトを割り当てようとすると、メモリが不足します。

また、一部のメモリは JRE によってすでに使い果たされています。今日のメモリ サイズでは 200k は問題ではなく、10M ヒープはほとんどのアプリケーションにとって非現実的なほど小さいです。

配列の実際のオーバーヘッドは比較的小さく、32 ビット VM では 12 バイトの IIRC (さらに、サイズが最小粒度 (AFAIK 8 バイト) よりも小さい場合に無駄になるもの) です。したがって、最悪の場合、配列ごとに 19 バイトのオーバーヘッドが発生します。

Java には 2D (多次元) 配列がないことに注意してください。Java はこれを配列の配列として内部的に実装します。

于 2012-08-03T17:23:23.523 に答える
1

2D の場合、より多くの小さなオブジェクトを割り当てます。メモリ マネージャーは、単一の大きなオブジェクトがヒープの大部分を占めていることに反対しています。これが好ましくない理由は、ガベージ コレクション スキームの詳細です。おそらく、そのようなものが世代間で小さなオブジェクトを移動でき、ヒープが単一の大きなオブジェクトの移動に対応できないためです。

于 2012-08-03T17:23:13.677 に答える
0

これは、メモリの断片化と、JVM が現在のヒープでそのサイズの配列を割り当てることができないことが原因である可能性があります。

ヒープのx長さが 10 であると想像してください。

xxxxxxxxxx 

次に、オブジェクトを0どこかに割り当てます。これにより、ヒープは次のようになります。

xxxxxxx0xx

xこれで、これらの 10 個のスペースを割り当てることができなくなりました。x使用可能なメモリが 9 秒であるにもかかわらず、8 秒を割り当てることさえできませんx

実際には、配列の配列は連続していないため、同じ問題に悩まされることはありません。

編集:上記は問題の非常に単純化された見方であることに注意してください。ヒープにスペースが必要な場合、Java のガベージ コレクターはできるだけ多くのメモリを収集しようとし、本当に必要な場合はヒープを圧縮しようとします。ただし、一部のオブジェクトは移動または収集できない可能性があり、ヒープの断片化が発生し、上記の状況に陥ります。

考慮しなければならない要因は他にもたくさんあります。その中には、VM でのメモリ リーク (ほとんどありません) またはアプリケーションでのメモリ リーク (単純なシナリオではほとんどありません)、使用の信頼性の低さRuntime.freeMemory()(GC が正しく実行される可能性があります) などがあります。呼び出し後、使用可能な空きメモリが変わる可能性があります)、特定の各 JVM の実装の詳細など。

ポイントは、経験則として、Runtime.freeMemory()アプリケーションで利用可能なすべての量を常に期待するとは限らないということです。

于 2012-08-03T17:26:24.443 に答える