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


14

Windowsの10の下のコードを実行している/ OpenJDKの11.0.4_x64は出力として生成used: 197expected 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で少し深く見ると、最初のケースではすべてが期待どおりに表示されます。

バイト配列は200MBを占めます

2番目のケースでは、バイト配列に加えて、バイト配列と同じ量のRAMを使用する同じ数のint配列が表示されます。

int配列はさらに200MBを占有します

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

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


ArrayList <byte []>からbyte [blocks] []にデータを変更し、forループで次のように変更してみてください
。data

空間的な局所性を向上さint[]せるために大きなものをエミュレートするbyte[]ために内部でJVMを使用することに何か関係があるのでしょうか?
Jacob G.

@JacobG。間違いなく内部的に見えますが、ガイド何も表示されいないようです。
カヤマン、

ただ2つの観察:1. 1024 * 1024から16を引くと、期待どおりに動作するようです。2. jdk8での動作は、ここで確認できる動作とは異なるようです。
第二

@secondうん、魔法の限界は明らかに、アレイが1MBのRAMを占めるかどうかです。1だけ減算すると、ランタイム効率や配列の管理オーバーヘッドが1 MBになるようにメモリが埋め込まれると思います。JDK8の動作が異なるのはおかしいです。
Georg、

回答:


9

これが説明しているのは、G1ガベージコレクターの標準の動作であり、通常はデフォルトで1MBの「リージョン」になり、Java 9ではJVMのデフォルトになりました。他の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


シリアルGCと8MBを必要とする長い配列(サイズは1024-1024-2で問題ありません)で同じ問題があり、G1HeapRegionSizeを変更しても私のケースでは何も起こりませんでした
GotoFinal

これについてはよくわかりません。使用したJava呼び出しと上記のコードの出力をlong []で明確にできますか
drekbour

@GotoFinal、上記で説明されていない問題は見当たりません。long[1024*1024]G1で1600Mの予想される使用量を与えるコードをテストしました-XX:G1HeapRegionSize[1M使用:1887、2M 使用:2097、4M 使用:3358、8M 使用:3358、16M 使用:3363、32M 使用:1682]。-XX:+UseConcMarkSweepGC使用:1687.で-XX:+UseZGC使用:2105をして-XX:+UseSerialGC使用:1698
drekbour

gist.github.com/c0a4d0c7cfb335ea9401848a6470e816 GCオプションを変更せずにそのようにコード化すると、印刷されますused: 417 expected usage: 400が、削除する-2used: 470、約50MBが削除され、50 * 2
long

1
同じこと。違いは〜50MBで、50個の「巨大な」ブロックがあります。ここではGCの詳細です:* 1024 1024 - > [0.297s][info ][gc,heap ] GC(18) Humongous regions: 450->450* 1024から2 1024 - > [0.292s][info ][gc,heap ] GC(20) Humongous regions: 400->400それはちょうどで16のバイトを保存するために、別の1MBの領域に割り当てるこれらの最後の二つのlong力G1を証明している。
drekbour
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.