7

When I compile and run the following very simple Java program using OpenJDK 11 (Zulu distribution on 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());
    }
}

it looks like garbage collection is decreasing the amount of free memory:

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

This does not happen when I run it with Oracle's Java 8:

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

Why is that?

user118967
  • 4,895
  • 5
  • 33
  • 54
  • That's a massive drop. Maybe the JDK decided it can return memory back to the OS? It hasn't been doing that so far, but maybe Java 11 changed that? – Thilo Mar 16 '19 at 06:47
  • Apparently the G1 collector does that, and Java 12 will get more aggressive about it, too. https://stackoverflow.com/questions/30458195/does-gc-release-back-memory-to-os – Thilo Mar 16 '19 at 06:48

2 Answers2

14

The answer is: The GC of Java 11 (when explicitly called e.g. via System.gc()) can reduce the used memory of the Java process (in Java known as totalMemory).

In Java 8 the default garbage collector was not able to reduce the used memory of the Java process. Memory occupied by the Java process was never ever released. Only if you switch to the G1GC garbage collector (option '-XX:+UseG1GC') Java 8 is able to reduce the used memory of the Java process (if you manually call System.gc()).

The "free memory" is the memory occupied by the Java process but that is currently not used. Therefore if you perform a garbage collection and the memory occupied by Java is reduced the amount of free memory is reduced, too.

Therefore the common way to calculate the "free memory" of a Java process is to use

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

This way is independent of the current memory occupied by the Java process.

Robert
  • 39,162
  • 17
  • 99
  • 152
  • 1
    But keep in mind that `maxMemory()` may return `Long.MAX_VALUE` if no explicit limit has been configured. In that case, the operating system’s limit for a process would apply. – Holger Mar 18 '19 at 10:58
2

I think what you are seeing is the JDK deciding that your heap is way too big for your application's needs and returning chunks of it to the operating system, thus reducing your Java heap size and as a side effect also the free/unused heap memory.

Thilo
  • 257,207
  • 101
  • 511
  • 656