Почему сборщик мусора Java OpenJDK 11 *уменьшает* свободную память в этом примере программы?

Когда я компилирую и запускаю следующую очень простую программу Java с использованием OpenJDK 11 (дистрибутив Zulu в Windows 10):

public class GCTest {
    public static void main(String[] args) {
        System.out.println("Free memory before garbage collection: " + Runtime.getRuntime().freeMemory());
        Runtime.getRuntime().gc();
        System.out.println("Free memory  after garbage collection: " + Runtime.getRuntime().freeMemory());
    }
}

похоже, что сборка мусора уменьшает объем свободной памяти:

Free memory before garbage collection: 266881496
Free memory  after garbage collection: 7772200

Этого не происходит, когда я запускаю его с Oracle Java 8:

Free memory before garbage collection: 254741016
Free memory  after garbage collection: 255795064

Почему это?


person user118967    schedule 16.03.2019    source источник
comment
Это огромное падение. Может быть, JDK решил, что может вернуть память ОС? До сих пор этого не было, но, может быть, Java 11 изменила это?   -  person Thilo    schedule 16.03.2019
comment
По-видимому, сборщик G1 делает это, и Java 12 также станет более агрессивным в этом отношении. stackoverflow.com/questions/30458195 /   -  person Thilo    schedule 16.03.2019


Ответы (2)


Ответ таков: GC Java 11 (при явном вызове, например, через System.gc()) может уменьшить используемую память процесса Java (в Java, известного как totalMemory).

В Java 8 сборщик мусора по умолчанию не мог уменьшить используемую память процесса Java. Память, занимаемая процессом Java, никогда не освобождалась. Только при переключении на сборщик мусора G1GC (опция '-XX:+UseG1GC') Java 8 способна уменьшить используемую память процесса Java (если вручную вызывать System.gc()).

«Свободная память» — это память, занятая процессом Java, но в настоящее время не используемая. Поэтому, если вы выполняете сборку мусора и память, занимаемая Java, уменьшается, объем свободной памяти также уменьшается.

Поэтому общепринятым способом вычисления «свободной памяти» процесса Java является использование

Runtime r = Runtime.getRuntime();
long free = r.maxMemory() - r.totalMemory() + r.freeMemory();

Этот способ не зависит от текущей памяти, занимаемой Java-процессом.

person Robert    schedule 16.03.2019
comment
Превосходно! Благодарю вас! - person user118967; 17.03.2019
comment
Но имейте в виду, что maxMemory() может вернуть Long.MAX_VALUE, если не было настроено явное ограничение. В этом случае будет применяться ограничение операционной системы для процесса. - person Holger; 18.03.2019

Я думаю, что вы видите, что JDK решает, что ваша куча слишком велика для нужд вашего приложения, и возвращает ее фрагменты в операционную систему, тем самым уменьшая размер вашей кучи Java и, как побочный эффект, также свободную/неиспользуемую память кучи.

person Thilo    schedule 16.03.2019