Is there a way to lower Java heap when not in use?

JavaMemory ManagementHeap Memory

Java Problem Overview


I'm working on a Java application at the moment and working to optimize its memory usage. I'm following the guidelines for proper garbage collection as far as I am aware. However, it seems that my heap seems to sit at its maximum size, even though it is not needed.

My program runs a resource intensive task once an hour, when the computer is not in use by a person. This task uses a decent chunk of memory, but then frees it all immediately after the task completes. The NetBeans profiler reveals that memory usage looks like this:

Java program memory usage

I'd really like to give all of that heap space back to the OS when not in use. There is no reason for me to hog it all while the program won't even be doing anything for at least another hour.

Is this possible? Thanks.

Java Solutions


Solution 1 - Java

You could perhaps play around with -XX:MaxHeapFreeRatio - this is the maximum percentage (default 70) of the heap that is free before the GC shrinks it. Perhaps setting it a bit lower (40 or 50?) and then using System.gc() might go some lengths to get you the desired behaviour?

There's no way to force this to happen however, you can try and encourage the JVM to do so but you can't just yank memory away as and when you want to. And while the above may shrink the heap, that memory won't necessarily be handed straight back to the OS (though in recent implementations of the JVM it does.)

Solution 2 - Java

Short version: Yes you can.

Long version:

How Java/JVM manages memory

For most applications the JVM defaults are okay. It looks like the JVM expects applications to run only a limited period of time. Therefore it does not seem to free memory on it's own.

In order to help the JVM to decide how and when to perform garbage collection, the following parameters should be supplied:

  • -Xms Specifies the minimal heap size
  • –Xmx Specifies the maximal heap size

For server applications add: -server

That's not enough for me. I want more control!

In case the above mentioned parameters are not enough, you can influence the behavior of the JVM regarding garbage collection.

First you can use System.gc() to tell the VM when you believe garbage collection would make sense. And second you can specify which of the garbage collectors the JVM should use:

Different Kinds of Garbage collectors:

  • Serial GC

    Command line parameter: -XX:+UseSerialGC

    Stops your application and performs GC.

  • Parallel GC

    Command line parameter: -XX:+UseParallelGC -XX:ParallelGCThreads=value

    Runs minor collections in parallel with your application. Reduces time needed for major collections, but uses another thread.

  • Parallel Compacting GC

    Command line parameter: -XX:+UseParallelOldGC

    Runs major collections in parallel with your application. Uses more CPU resources, reduces memory usage.

  • CMS GC

    Command line parameter: -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=value -XX:+UseCMSInitiatingOccupancyOnly

    Performs smaller collections, and more often than Serial GC, thus limiting the breaks/stops of the application.

  • G1

    Command line parameter: -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC

    Experimental (at least in Java 1.6): Tries to make sure the application is never stopped for more than 1s. #Example Memory usage of a Play Framework web application without any optimizations: Play Framework WebApp without optimizations As you can see, it uses quite a lot of heap space, and the used space is regularly freed.

In this case the optimizations with parameters only were not effective. There were some scheduled tasks which used rather a lot of memory. In this case the best performance was achieved by using the CMS GC combined with System.gc() after the memory intensive operations. As a result the memory usage of the WebApp was reduced from 1.8 GB to around 400-500 MB.

You can see here another screenshot from the VisualVM which shows how memory is freed by the JVM and actually returned to the OS:

Memory is being freed by the JVM and returned to the OS Note: I used the "Perform GC"-button of the VisualVM to perform the GC rather than System.gc() in my code, as the scheduled tasks which consume the memory are only launched at specific times and somewhat harder to capture with VisualVM.

Further Reading

Solution 3 - Java

Java 12 supports this feature using G1GC.

> JEP 346: Promptly Return Unused Committed Memory from G1.

>Enhance the G1 garbage collector to automatically return Java heap memory to the operating system when idle.

https://openjdk.java.net/jeps/346

Java 13 supports this feature using zgc

> JEP 351: ZGC: Uncommit Unused Memory

> ZGC does not currently uncommit and return memory to the operating system, even when that memory has been unused for a long time. This behavior is not optimal for all types of applications and environments, especially those where memory footprint is a concern. For example: Container environments where resources are paid by use.

> - Environments where an application might be idle for long periods of time and is sharing or competing for resources with many other applications.

> - An application might have very different heap space requirements during its execution. For example, the heap needed during start up might be greater than what is needed later during steady state execution.

http://openjdk.java.net/jeps/351

Solution 4 - Java

> The JVM doesn't work that way. You can't give it back to the OS.

As noted by several people since this was written four years ago, you can give memory back to the OS if you give the proper GC settings to the JVM.

Solution 5 - Java

One possibility is to have your background java application launch an external jvm instance each hour to run your task. That way only your original jvm application is running between tasks.

Solution 6 - Java

If your app is quiescent during periods of inactivity, it's possible the OS will swap out those pages for you, mitigating their pressure on physical memory.

http://www.linuxvox.com/2009/10/what-is-the-linux-kernel-parameter-vm-swappiness/

Solution 7 - Java

Java best kept secret: -Xincgc It does impact performance but not always that much. Sometimes it does, depends on what you're doing. The incremental garbage collector hands memory back to the system quite well !

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjocullView Question on Stackoverflow
Solution 1 - JavaMichael BerryView Answer on Stackoverflow
Solution 2 - JavaPeanutView Answer on Stackoverflow
Solution 3 - JavaAlexBView Answer on Stackoverflow
Solution 4 - JavaduffymoView Answer on Stackoverflow
Solution 5 - Javarobert_x44View Answer on Stackoverflow
Solution 6 - JavaRonView Answer on Stackoverflow
Solution 7 - JavaMikeView Answer on Stackoverflow