Windows 10/OpenJDK 11.0.4_x64で以下のコードを実行すると、出力としてused: 197
およびexpected usage: 200
が生成されます。これは、100万要素の200バイト配列が約100を占めることを意味します。 200MB RAM。すべて元気です。
コードのバイト配列の割り当てをnew byte[1000000]
からnew byte[1048576]
に(つまり、1024 * 1024要素に)変更すると、出力としてused: 417
およびexpected usage: 200
が生成されます。一体何ですか?
import Java.io.IOException;
import Java.util.ArrayList;
public class Mem {
private static Runtime rt = Runtime.getRuntime();
private static long free() { return rt.maxMemory() - rt.totalMemory() + rt.freeMemory(); }
public static void main(String[] args) throws InterruptedException, IOException {
int blocks = 200;
long initiallyFree = free();
System.out.println("initially free: " + initiallyFree / 1000000);
ArrayList<byte[]> data = new ArrayList<>();
for (int n = 0; n < blocks; n++) { data.add(new byte[1000000]); }
System.gc();
Thread.sleep(2000);
long remainingFree = free();
System.out.println("remaining free: " + remainingFree / 1000000);
System.out.println("used: " + (initiallyFree - remainingFree) / 1000000);
System.out.println("expected usage: " + blocks);
System.in.read();
}
}
Visualvmで少し深く見ると、最初のケースではすべてが期待どおりに表示されます。
2番目のケースでは、バイト配列に加えて、バイト配列と同じ数のRAMを占めるint配列の数がわかります。
ちなみに、これらのint配列は、それらが参照されていることを示していませんが、ガベージコレクションはできません...
ここで何が起こっているのでしょうか?
これが説明するのは G1ガベージコレクター の標準の動作であり、これは一般にデフォルトで1MBの「リージョン」になり、Java 9 。他のGCを有効にして実行すると、数値が異なります。
領域サイズの半分を超えるオブジェクトは「巨大」と見なされます...ヒープ領域サイズの倍数よりわずかに大きいオブジェクトの場合、この未使用のスペースによりヒープが断片化する可能性があります。
私はJava -Xmx300M -XX:+PrintGCDetails
を実行しましたが、ヒープが巨大な領域によって使い果たされていることを示しています。
[0.202s][info ][gc,heap ] GC(51) Old regions: 1->1
[0.202s][info ][gc,heap ] GC(51) Archive regions: 2->2
[0.202s][info ][gc,heap ] GC(51) Humongous regions: 296->296
[0.202s][info ][gc ] GC(51) Pause Full (G1 Humongous Allocation) 297M->297M(300M) 1.935ms
[0.202s][info ][gc,cpu ] GC(51) User=0.01s Sys=0.00s Real=0.00s
...
Exception in thread "main" Java.lang.OutOfMemoryError: Java heap space
1MiB byte[]
を「G1領域サイズの半分未満」にしたいので、-XX:G1HeapRegionSize=4M
を追加すると機能的なアプリケーションが得られます。
[0.161s][info ][gc,heap ] GC(19) Humongous regions: 0->0
[0.161s][info ][gc,metaspace ] GC(19) Metaspace: 320K->320K(1056768K)
[0.161s][info ][gc ] GC(19) Pause Full (System.gc()) 274M->204M(300M) 9.702ms
remaining free: 100
used: 209
expected usage: 200
G1の概要: https://www.Oracle.com/technical-resources/articles/Java/g1gc.html