Runtime.getRuntime (). MaxMemory () Метод вычисления - PullRequest
0 голосов
/ 25 октября 2018

Вот код:

    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 version "1.8.0_181"
    Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
    [root@docker-runner-2486794196-0fzm0 docker-runner]# java -jar -Xmx1024M -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap  test.jar
    Runtime max: 954728448 (910.50 M)
    Non-heap: -1 (-0.00 M)
    Heap: 954728448 (910.50 M)
    Pool: Code Cache (type Non-heap memory) = 251658240 (240.00 M)
    Pool: Metaspace (type Non-heap memory) = -1 (-0.00 M)
    Pool: Compressed Class Space (type Non-heap memory) = 1073741824 (1024.00 M)
    Pool: PS Eden Space (type Heap memory) = 355467264 (339.00 M)
    Pool: PS Survivor Space (type Heap memory) = 1048576 (1.00 M)
    Pool: PS Old Gen (type Heap memory) = 716177408 (683.00 M)
    

* Максимальное время выполнения: 954728448 (910,50 М) *

Runtime.maxMemory - 910.50M , Я хочу знать, как это работает

На JDK7, "Runtime.getRuntime (). MaxMemory ()" = "-Xmx" -"Survivor" , но он не работает на JDK8。

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

В JDK 8 формула Runtime.maxMemory() = Xmx - Survivor все еще справедлива, но хитрость заключается в том, как оценивается Survivor.

Вы не установили начальный размер кучи (-Xms) и политику адаптивного размеравключен по умолчанию.Это означает, что размер кучи может изменяться, а границы генерации кучи могут перемещаться во время выполнения.Runtime.maxMemory() оценивает объем памяти консервативно, вычитая максимально возможный размер выжившего из размера New Generation.

Runtime.maxMemory() = OldGen + NewGen - MaxSurvivor

  where MaxSurvivor = NewGen / MinSurvivorRatio

В вашем примере OldGen = 683 МБ, NewGen = 341 МБи MinSurvivorRatio = 3 по умолчанию.То есть

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

Если вы отключите -XX:-UseAdaptiveSizePolicy или установите начальный размер кучи -Xms равным значению -Xmx, вы снова увидите, что Runtime.maxMemory() = OldGen + Eden + Survivor.

0 голосов
/ 25 октября 2018

Предположение о том, что несоответствие между сообщаемой максимальной кучей и фактической максимальной кучей связано с пространством выживших, было основано на эмпирических данных, но не было доказано как преднамеренная особенность.

Я расширил программунемного (код в конце).Запуск этой расширенной программы на 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, заключалась в том, что термин «штатный» в связанном ответе дал мне подсказку о том, откуда пришло это предположение.Поскольку, когда я запускаю программу на Java 6 без -XX:-UseParallelGC на моей машине, я получаю

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)

Здесь максимальный размер, о котором сообщается, не равен сумме пулов динамической памяти, следовательно, «формула «Максимальный размер плюс Выживший» дает другой результат.Это те же значения, которые я получаю в 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 дает точный размер кучи.Для сборщика «Сначала мусор», где формула неприменима, сообщенный максимальный размер уже является правильным значением.

Таким образом, наилучшей стратегией является получение максимальных значений для Eden, SurvivorTenured (он же 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);
}
...