【问题标题】:Runtime.getRuntime().maxMemory() Calculate MethodRuntime.getRuntime().maxMemory() 计算方法
【发布时间】:2019-03-29 13:20:16
【问题描述】:

代码如下:

    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()));
    }  

在JDK8上运行的代码是:

[root@docker-runner-2486794196-0fzm0 docker-runner]# java -version java版本“1.8.0_181” Java(TM) SE 运行时环境 (build 1.8.0_181-b13) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.181-b13,混合模式) [root@docker-runner-2486794196-0fzm0 docker-runner]# java -jar -Xmx1024M -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap test.jar 最大运行时间:954728448 (910.50 M) 非堆:-1 (-0.00 M) 堆:954728448 (910.50 M) 池:代码缓存(类型非堆内存)= 251658240 (240.00 M) 池:元空间(类型非堆内存)= -1 (-0.00 M) 池:压缩类空间(类型非堆内存)= 1073741824 (1024.00 M) 池:PS Eden Space(堆内存类型)= 355467264 (339.00 M) 池:PS Survivor Space(堆内存类型)= 1048576 (1.00 M) 池:PS Old Gen(堆内存类型)= 716177408 (683.00 M)

*运行时间最大值:954728448 (910.50 M) *

Runtime.maxMemory 是 910.50M,我想知道这是怎么回事

On JDK7, "Runtime.getRuntime().maxMemory()" = "-Xmx" - "Survivor" ,但是在JDK8上不行。

【问题讨论】:

  • 不确定这是否相关,但似乎您是在 docker 容器中运行它,并且您同时使用 -XX:+UseCGroupMemoryLimitForHeap-Xms,如图所示 here 将覆盖另一个
  • 感谢您的回复!此代码未在 docker 上运行,将具有相同的结果 #java -jar -Xmx1024M test.jar Runtime max: 954728448 (910.50 M) ... Pool: PS Old Gen (type Heap memory) = 716177408 (683.00 M) )
  • 对不起,我已经更新了我的问题

标签: java-8 jvm jvm-hotspot


【解决方案1】:

在 JDK 8 中,公式 Runtime.maxMemory() = Xmx - Survivor 仍然是公平的,但诀窍在于如何估计 Survivor。

您尚未设置初始堆大小 (-Xms),自适应大小策略默认处于启用状态。这意味着堆可以调整大小并且堆生成边界可以在运行时移动。 Runtime.maxMemory() 保守地估计内存量,从新一代的大小中减去最大可能幸存者大小。

Runtime.maxMemory() = OldGen + NewGen - MaxSurvivor

  where MaxSurvivor = NewGen / MinSurvivorRatio

在您的示例中,默认情况下 OldGen = 683 MB、NewGen = 341 MB 和 MinSurvivorRatio = 3。也就是说,

Runtime.maxMemory() = 683 + 341 - (341/3) = 910.333 MB

如果您禁用-XX:-UseAdaptiveSizePolicy 或将初始堆大小-Xms 设置为与-Xmx 相同的值,您将再次看到Runtime.maxMemory() = OldGen + Eden + Survivor

【讨论】:

  • 非常感谢。你的回答解决了我的困惑。
【解决方案2】:

报告的最大堆与实际最大堆之间的差异源于幸存者空间的假设是基于经验数据,但尚未被证明是有意的特征。

我稍微扩展了程序(代码在最后)。在JDK 6-Xmx1G -XX:-UseParallelGC 上运行这个扩展程序给了我

Runtime max: 1037959168 (989 MiB)
Heap: 1037959168 (989 MiB)
Pool: Eden Space = 286326784 (273 MiB)
Pool: Survivor Space = 35782656 (34 MiB)
Pool: Tenured Gen = 715849728 (682 MiB)
Pool: Heap memory total = 1037959168 (989 MiB)
Eden + 2*Survivor + Tenured = 1073741824 (1024 MiB)

(Non-heap: omitted)

在这里,值匹配。报告的最大大小等于堆空间的总和,因此报告的最大大小和一个幸存者空间的大小之和等于公式Eden + 2*Survivor + Tenured 的结果,即精确的堆大小。

我指定-XX:-UseParallelGC 的原因是,链接答案的“Tenured”一词暗示了这个假设的来源。因为,当我在没有 -XX:-UseParallelGC 的机器上运行 Java 6 上的程序时,我得到了

Runtime max: 954466304 (910 MiB)
Heap: 954466304 (910 MiB)
Pool: PS Eden Space = 335609856 (320 MiB)
Pool: PS Survivor Space = 11141120 (10 MiB)
Pool: PS Old Gen = 715849728 (682 MiB)
Pool: Heap memory total = 1062600704 (1013 MiB)
Eden + 2*Survivor + Tenured = 1073741824 (1024 MiB)

(Non-heap: omitted)

这里,报告的最大大小不等于堆内存池的总和,因此“报告的最大大小加上 Survivor”公式会产生不同的结果。这些是相同的值,我使用默认选项使用 Java 8,因此您的问题与 Java 8 无关,因为即使在 Java 6 上,当垃圾收集器与链接问答中使用的垃圾收集器不同时,这些值也不匹配。

请注意,从 Java 9 开始,-XX:+UseG1GC 成为默认设置,因此我得到了

Runtime max: 1073741824 (1024 MiB)
Heap: 1073741824 (1024 MiB)
Pool: G1 Eden Space = unspecified/unlimited
Pool: G1 Survivor Space = unspecified/unlimited
Pool: G1 Old Gen = 1073741824 (1024 MiB)
Pool: Heap memory total = 1073741824 (1024 MiB)
Eden + 2*Survivor + Tenured = N/A

(Non-heap: omitted)

底线是,差异等于幸存者空间大小的假设仅适用于一个特定的(过时的)垃圾收集器。但如果适用,公式Eden + 2*Survivor + Tenured 会给出确切的堆大小。对于“垃圾优先”收集器,在公式不适用的情况下,报告的最大大小已经是正确的值。

所以最好的策略是获取EdenSurvivorTenured(又名Old)的最大值,然后检查这些值是否为-1。如果是,就使用Runtime.getRuntime().maxMemory(),否则,计算Eden + 2*Survivor + Tenured

程序代码:

public static void main(String[] args) {
    System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));
    MemoryMXBean m = ManagementFactory.getMemoryMXBean();
    System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));
    scanPools(MemoryType.HEAP);
    checkFormula();
    System.out.println();
    System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));
    scanPools(MemoryType.NON_HEAP);
    System.out.println();
}

private static void checkFormula() {
    long total = 0;
    boolean eden = false, old = false, survivor = false, na = false;
    for(MemoryPoolMXBean mp: ManagementFactory.getMemoryPoolMXBeans()) {
        final long max = mp.getUsage().getMax();
        if(mp.getName().contains("Eden")) { na = eden; eden = true; }
        else if(mp.getName().matches(".*(Old|Tenured).*")) { na = old; old = true; }
        else if(mp.getName().contains("Survivor")) {
            na = survivor;
            survivor = true;
            total += max;
        }
        else continue;
        if(max == -1) na = true;
        if(na) break;
        total += max;
    }
    System.out.println("Eden + 2*Survivor + Tenured = "
        +(!na && eden && old && survivor? mb(total): "N/A"));
}

private static void scanPools(final MemoryType type) {
    long total = 0;
    for(MemoryPoolMXBean mp: ManagementFactory.getMemoryPoolMXBeans()) {
        if(mp.getType()!=type) continue;
        long max = mp.getUsage().getMax();
        System.out.println("Pool: "+mp.getName()+" = "+mb(max));
        if(max != -1) total += max;
    }
    System.out.println("Pool: "+type+" total = "+mb(total));
}

private static String mb(long mem) {
    return mem == -1? "unspecified/unlimited":
        String.format("%d (%d MiB)", mem, mem>>>20);
}

【讨论】:

    猜你喜欢
    • 2012-12-08
    • 2019-07-02
    • 2013-07-06
    • 2021-08-03
    • 1970-01-01
    • 2018-02-18
    • 2015-07-30
    • 2014-03-16
    • 1970-01-01
    相关资源
    最近更新 更多