web-dev-qa-db-ja.com

-XmxとRuntime.maxMemoryが一致しないのはなぜですか

追加するとき

 -Xmx????m

コマンドラインに対して、JVMはこの値に近いヒープを提供しますが、最大14%アウトする可能性があります。 JVMは、試行錯誤によってのみ、必要な数値にはるかに近い数値を提供できます。

 System.out.println(Runtime.getRuntime().maxMemory());

プリント

-Xmx1000m ->  932184064
-Xmx1024m -Xmx1g ->  954728448
-Xmx1072m ->  999292928
-Xmx1073m -> 1001390080

HotSpot Java 8 update5を実行しています。

明らかに、ヒープは1000000000のすぐ上にある可能性がありますが、なぜこれは-Xmx1073mではなく-Xmx1000mなのですか?

ところで、1g == 1024mは、1gが1024 ^ 3であり、1000 ^ 3より7%高いが、1000 ^ 3より7%低いことを示唆しています。


あまりにも離れているということは、ヒープがどのように機能するかについて基本的な何かが欠けていることを示唆しています。 -Xmx1000mを要求し、それが1001390080だった場合、気にしないでください。準拠する必要のある割り当ての倍数があると思いますが、932184064を与えると、ヒープがもっと多いことがわかります想像以上に複雑です。


編集私はそれを見つけました

-Xmx1152m gives 1073741824 which is exactly 1024^3

したがって、この場合はmaxMemory()を参照すると、要求したよりも正確に128MB少なくなっているようです。


ところで128は私のお気に入りの番号です。私は今日、番地128で会議に出席し、スピーカーは128ページから本を引用しました;)

25
Peter Lawrey

この違いは、ガベージコレクターのサバイバースペースのサイズによって説明されているようです。

docs で説明されているように、-Xmxフラグは、メモリ割り当てプールの最大サイズを制御します。 メモリ割り当てプール のヒープ部分は、Eden、Survivor、およびTenuredスペースに分割されます。 この回答 で説明されているように、2つのサバイバーリージョンがあり、そのうちの1つだけが任意の時点でライブオブジェクトを保持するために使用できます。したがって、Runtime.maxMemory()によって報告されるように、オブジェクトの割り当てに使用できる見かけの合計スペースは、ヒープメモリプールの合計からサバイバースペースの1つのサイズを差し引く必要があります。

MemoryMXBeanクラスとMemoryPoolMXBeanクラスを使用して、メモリ割り当てに関するもう少し情報を取得できます。これが私が書いた簡単なプログラムです:

import Java.lang.management.ManagementFactory;
import Java.lang.management.MemoryMXBean;
import Java.lang.management.MemoryPoolMXBean;

public class MemTest {
  static String mb (long s) {
    return String.format("%d (%.2f M)", s, (double)s / (1024 * 1024));
  }

  public static void main(String[] args) {
    System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));
    MemoryMXBean m = ManagementFactory.getMemoryMXBean();

    System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));
    System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));

    for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {
      System.out.println("Pool: " + mp.getName() + 
                         " (type " + mp.getType() + ")" +
                         " = " + mb(mp.getUsage().getMax()));
    }
  }
}

Java -Xmx1024m MemTestのOpenJDK7でのこれの出力は次のとおりです。

Runtime max: 1037959168 (989.88 M)
Non-heap: 224395264 (214.00 M)
Heap: 1037959168 (989.88 M)
Pool: Code Cache (type Non-heap memory) = 50331648 (48.00 M)
Pool: Eden Space (type Heap memory) = 286326784 (273.06 M)
Pool: Survivor Space (type Heap memory) = 35782656 (34.13 M)
Pool: Tenured Gen (type Heap memory) = 715849728 (682.69 M)
Pool: Perm Gen (type Non-heap memory) = 174063616 (166.00 M)

Eden + 2 * Survivor + Tenured = 1024Mであることに注意してください。これは、コマンドラインで要求されたヒープスペースの量とまったく同じです。これを指摘してくれた@ Absurd-Mindに感謝します。

異なるJVM間で観察される違いは、さまざまな世代のデフォルトの相対サイズを選択するためのヒューリスティックが異なるためである可能性があります。 この記事 (Java 6に適用され、最新のものを見つけることができませんでした))で説明されているように、-XX:NewRatioフラグと-XX:SurvivorRatioフラグを使用して明示的に制御できますこれらの設定。したがって、コマンドを実行します。

Java -Xmx1024m -XX:NewRatio=3 -XX:SurvivorRatio=6

あなたはJVMに次のことを伝えています:

Young:Tenured = (Eden + 2*Survivor):Tenured = 1:3 = 256m:768m
Survivor:Eden = 1:6 = 32m:192m

したがって、これらのパラメータを使用すると、要求された-Xmx値とRuntime.maxMemory()によって報告された使用可能なメモリの差は32mになります。これは、上記のプログラムを使用して確認されます。これで、コマンドライン引数の特定のセットに対してRuntimeによって報告された使用可能なメモリを正確に予測できるはずです。これは、本当に必要なことですよね?

26
Alex

次のグラフは、Runtime.maxMemoryをさまざまなXmx値(x軸)に対するXmx(y軸)のパーセンテージとして示しています。

ほとんどの場合、ヒープの拡張に使用できるのはXmx設定の約85%のみであることがわかります。この分析は、Javaバージョン "1.8.0_212" Java(TM)SEランタイム環境(ビルド1.8.0_212-b10)Java HotSpot(TM))を使用して実行されます。 64ビットサーバーVM(ビルド25.212-b10、混合モード)

さまざまなXmxサイズ(MB単位)の(maxMem/Xmx)のグラフ

0
Rayman Preet