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:
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.
You can change size of heap space by using JVM options -Xms and -Xmx. Xms denotes starting size of Heap while -Xmx denotes maximum size of Heap in Java.
Preferably the heap should be at least twice the size of the total amount of live objects, or large enough so that the JVM spends less time garbage collecting the heap than running Java code. To allow the heap to grow and shrink depending on the amount of free memory in your system, set -Xms lower than -Xmx .
Java objects reside in an area called the heap. The heap is created when the JVM starts up and may increase or decrease in size while the application runs. When the heap becomes full, garbage is collected. During the garbage collection objects that are no longer used are cleared, thus making space for new objects.
The permanent generation is the region of the heap where class definitions are stored.
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.)
Short version: Yes you can.
Long version:
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 sizeFor server applications add: -server
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:
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.
Memory usage of a Play Framework web application without any 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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With