62

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.

trincot
  • 317,000
  • 35
  • 244
  • 286
jocull
  • 20,008
  • 22
  • 105
  • 149
  • I think this depends on what JVM you're using. The JVM spec says nothing about this, IIRC. – templatetypedef Feb 10 '11 at 01:48
  • Does that mean once it's allocated it's there for the application's life cycle? – jocull Feb 10 '11 at 01:50
  • Please cite the JVM that gives memory back to the OS. I don't know of one. – duffymo Feb 10 '11 at 01:50
  • Anyway heapspace is in your virtual address space. If you don't use it for a while, it simply won't be brought in into physical RAM. In short, effectively OS will claim it anyway. You don't have to give back. I don't think you are worried about swap [disk] space anyway. – Fakrudeen Feb 10 '11 at 08:39
  • 3
    @Fakrudeen, what you said is incorrect. If it is not decommit by process, OS may reclaim the physical memory, but it needs to write the content to disk first, which degrades performance a lot. For many servers it doesn't have swap to make sure it doesn't thrashing. In this case, OS can never reclaim the memory, however long it is untouched. Your comment will mislead anyone who didn't take OS course before. It's harmful. – Kan Li Oct 01 '13 at 04:49
  • @icando - I stand by what I said. It will write to swap but then doesn't have to bring it back as long as it is not used again. I am not advising anyone to leak memory left and right. But in this case he is worried about the memory which is already released. – Fakrudeen Oct 01 '13 at 15:45
  • 3
    @Fakrudeen, swapping unused memory to disk is always bad idea. It incurs IO when writing it to disk. More importantly, if JVM needs more memory again, it needs to read garbage content from disk. If JVM can shrink and avoid being swapped, then if it needs more memory, it can ask OS to allocate free pages to them, which is much cheaper than reading garbage content from disk. I feel sad that you guys feel OK about things being swapped. What if a newbie reads about this and had a life-time impression that it is not a big deal things got swapped. – Kan Li Oct 01 '13 at 18:23
  • @icando - World is about trade-offs. How will JVM know it is unused till it runs a huge GC task? If swapping is bad keeping it in memory and running a huge GC task is even worse as it will really screw other processes in terms of physical memory and CPU. It is DMA which loads/store from Disk not CPU. What if a new bie reads your comment and think swapping is such a bad thing that running GC every so often is not a big deal and writes System.GC() at the end of all his functions [even if it is only advisory call]. – Fakrudeen Oct 04 '13 at 11:01
  • 3
    @Fakrudeen, look at OP's chart, JVM does GC after his resource intensive job finishes, and JVM definitely knows how much memory it is using. I never say GC is not expensive. The problem we are talking about is not whether GC is expensive or not, it is after GC, JVM knows it has a bunch of free heap, but never returns it to OS. Man, I know people is instinctively defensive when criticized. But please think about it. Also, I want newbie to know that swapping is REALLY bad thing. If some process has heavy disk IO, almost every other process will hang, e.g. you can't even SSH to the machine. – Kan Li Oct 05 '13 at 01:19
  • You can actually return memory to the OS by configuring the JVM properly. (-> have a look at my answer) – Peanut Aug 28 '15 at 06:59
  • Don't want to fire a very old fight again, but I think it really ought to be said that Java's GC (no mater which GC strategy is used) plays very badly with swapping. GC engines need to periodically inspect all of its heap to determine which objects are still alive, and possibly to move things around. If part of the heap goes to swap, then the GC will constantly force these to be paged back in, which is a very costly operation; actually, if GC heaps represent a large proportion of that machine's swap needs (and that can be from multiple Java processes), then the system is about to go trashing. – James Sep 19 '18 at 15:33
  • This is why Java heap size should always be determined on the base of a machine's physically installed RAM, and take into account other similar process running concurrently. – James Sep 19 '18 at 15:35
  • Indeed, some generational GC strategies deals reasonably well when _small portions_ of the heap is swapped to disk, but this should definitely not be relied on. At this time, mainstream JREs offer no GC strategy that is swap-aware; research effort as been done in this regard, but these require modifications at the kernel level, which makes them non-viable for mainstream programs. – James Sep 19 '18 at 15:41
  • 1
    Finally, note that all of this applies only to the Java's heap (that is, GC managed memory). Relying on swap for off-heap memory (for example, `ByteBuffer.allocateDirect()`) pose absolutely no issue form the GC's perspective, and may be perfectly acceptable (depending on the use case, obviously). This is for example what is done by all major Java-based databases, caches and search engines, though even these generally recommend that memory settings be adjusted to avoid anonymous pages from being swapped out. – James Sep 19 '18 at 15:49

7 Answers7

44

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.)

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
  • This is interesting, but it doesn't address the question itself which is "I'd really like to give all of that heap space back to the OS when not in use... Is this possible?"... GC directions just tell the JVM how to manage the (monotonically growing) Java heap. – andersoj Feb 10 '11 at 02:10
  • 11
    @andersoj: This setting _does_ give back RAM to the OS. See http://stopcoding.wordpress.com/2010/04/12/more-on-the-incredible-shrinking-jvm-heap for an impressive example. – A.H. Feb 21 '12 at 13:22
  • 1
    @A.H.: Too cool, too cool. I was unaware, thank you for the pointer. (Wish I was smart enough to improve my answer above to cover this... if you post an answer here I'll upvote.) – andersoj Feb 21 '12 at 15:24
  • 2
    @andersoj Please upvote this answer, because berry120 is the one who introduced that specific parameter. – A.H. Feb 21 '12 at 17:05
  • 5
    Another very interesting article, showing that `SerialGC` and `ParNewGC` give back memory: http://www.stefankrause.net/wp/?p=14 – Cobra_Fast Nov 20 '13 at 15:46
20

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

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Peanut
  • 3,753
  • 3
  • 31
  • 45
5

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

Heuriskos
  • 472
  • 3
  • 6
3

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.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 1
    I think this is a mix of sarcasm and also the truth? I have noticed that if I hit the garbage collect button on the profiler about 50 times I can get the heap back down to a reasonable level. – jocull Feb 10 '11 at 01:47
  • 2
    Getting the (Java) heap down to a lower level doesn't give the memory back to the OS. The effect is the same, though, since unused pages are swapped out and don't have a significant impact. – andersoj Feb 10 '11 at 01:49
  • 1
    No, truth. Not an ounce of sarcasm. As far as I know, the size of the heap may decrease, but the memory set aside for the JVM doesn't go back to the OS. – duffymo Feb 10 '11 at 01:49
  • So no other program will ever be allowed to make use of memory claimed by the JVM, even if the running Java program needs but a fraction of what the JVM has claimed? Am I correct? – jocull Feb 10 '11 at 01:53
  • Correct. From the OS point of view, it knows nothing about your Java program, only the JVM. That's what is executing. It so happens that the JVM is interpreting your Java bytecode, but the OS knows nothing about it. – duffymo Feb 10 '11 at 01:56
  • 2
    @jocull: JVM can garbage-collect and compact its heap; there'll be many unused pages. JVM can't return it to the OS as freed memory, but it will not use these pages. Running out of RAM, OS will swap these pages out, allowing active processes to use the physical RAM these pages used to occupy. This is not exactly 'freeing memory', and some disk I/O is involved, but the effect is similar: you give active processes some of the RAM that JVM occupied earlier. – 9000 Feb 10 '11 at 01:59
  • @9000: That works for me, as long as other processes can get to it, through paging or otherwise. Thanks. – jocull Feb 10 '11 at 02:12
  • 2
    I've seem the JVM give back memory to the OS under Windows XP. So it is possible. – Steve Kuo Feb 10 '11 at 05:04
  • All you can do is reduce the amount of memory you give the JVM in the first place. BTW: 80 MB is not alot of memory these days. Its worth about £6. – Peter Lawrey Feb 10 '11 at 09:30
  • 2
    -Xincgc does give memory back to the system ! – Mike Jul 28 '11 at 18:10
  • 9
    At least since Java 1.5 memory __CAN__ be returned to the OS. See http://stopcoding.wordpress.com/2010/04/12/more-on-the-incredible-shrinking-jvm-heap for an impressive example. – A.H. Feb 21 '12 at 13:23
  • 1
    This answer seems to be obsolete and I believe it should be updated, as recent JVMs actually can free up memory and return it to the system. – Peanut Jun 30 '15 at 08:15
3

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.

robert_x44
  • 9,224
  • 1
  • 32
  • 37
3

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/

Ron
  • 1,932
  • 20
  • 17
3

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 !

Mike
  • 2,393
  • 3
  • 25
  • 37
  • Thanks! I will look into that if I get a chance to go back to this project. – jocull Jul 28 '11 at 18:49
  • 3
    Note: Using OpenJDK 8, this prints out `OpenJDK 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release` – Peter V. Mørch Oct 22 '15 at 13:11