web-dev-qa-db-ja.com

Java 1 MB以上のバイト配列は2倍を占めるRAM

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で少し深く見ると、最初のケースではすべてが期待どおりに表示されます。

byte arrays take up 200mb

2番目のケースでは、バイト配列に加えて、バイト配列と同じ数のRAMを占めるint配列の数がわかります。

int arrays take up additional 200mb

ちなみに、これらのint配列は、それらが参照されていることを示していませんが、ガベージコレクションはできません...

ここで何が起こっているのでしょうか?

14
Georg

これが説明するのは 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

G1の詳細: https://docs.Oracle.com/en/Java/javase/13/gctuning/garbage-first-garbage-collector-tuning.html#GUID-2428DA90-B93D-48E6-B336 -A849ADF1C552

9
drekbour