69

When the garbage collector runs and releases memory does this memory go back to the OS or is it being kept as part of the process. I was under the strong impression that the memory is never actually released back to OS but kept as part of the memory area/pool to be reused by the same process.

As a result the actual memory of a process would never decrease. An article that reminded me was this and Java’s Runtime is written in C/C++ so I guess the same thing applies?

Update
My question is about Java. I am mentioning C/C++ since I assume the Java’s allocation/deallocation is done by JRE using some form of malloc/delete

Jim
  • 18,826
  • 34
  • 135
  • 254
  • 2
    Even using the standard heap allocation methods in C or C++ doesn't have to release back to the OS, and the OS can itself keep the freshly unallocated pages mapped to the process (so it doesn't seem to be free'd). – Some programmer dude May 26 '15 at 12:13
  • `Java’s Runtime is written in C/C++ so I guess the same thing applies?` And Java´s GC is written in C++ too... it´s not "below" C++ – deviantfan May 26 '15 at 12:17
  • Till now no heap memory is released to the OS. Merely memory is taken extra if the max heap size still is not reached. This is the same as in C, whereas C has the additional problem that memory reuse is a bit hampered with memory fragmentation (though C can do its own memory management of allocs). – Joop Eggen May 26 '15 at 12:28
  • 1
    JVM starts with a minimum heap size and we may set max and minimum heap-size to the same value. If max and min heap size is same, it will upfront allocate the JAVA heap and there actually may only few Java objects using this heap, in this case GC doesn't release memory back to OS. GC actually claims back memory used be un-referenced objects, so that they may used for other objects. It doesn't translate directly to freeing the memory back to OS. It may free the memory, by calling free from C-library. But malloc also internally still may not be immediately release the memory to the OS. – Nipun Talukdar May 26 '15 at 12:28
  • @JoopEggen:So a process never shrinks to help OS? – Jim May 26 '15 at 12:33
  • Yes, there also is no negation between active processes/OS AFAIK. The only measure taken is memory swapping (Unix, Windows); storing a part of the RAM on disk. – Joop Eggen May 26 '15 at 12:40
  • @JoopEggen:So if I alocate large collections and make sure I clean them up after use, I still can kill the whole system out of memory? – Jim May 26 '15 at 12:58
  • No the java.exe (in the longer run) will neither decrease or increase its heap usage; it has its own internal memory management. In the beginning it likely will increase its heap usage to the specified max usage. Hence a web server in java can run 24/7 nonstop. – Joop Eggen May 26 '15 at 13:11
  • @JoopEggen:But if the heap size is never decreasing that means that the system can not use memory that is not actually used/needed by the java app. Right? Isn’t that a problem? – Jim May 26 '15 at 13:55
  • It means that applications must not be too greedy; and a java app can start with a small heap. If the OS detects that RAM memory becomes rare, it **swaps** parts to disk, and on **page faults** reloads them piece wise. Note that every processes memory is owned and more or less protected. Hence it would be hard to manage smaller memory areas. – Joop Eggen May 26 '15 at 14:02
  • @JoopEggen:But there are cases that we can have "bursts" of memory need and then an extended "idle" period (example a server). In that case the application needed the memory and was not greedy. But does not anymore. So the system now will be degrading as a whole? – Jim May 26 '15 at 17:52
  • @JoachimPileborg:So a C++ process can also consume most of the memory in a system on the expense of other processes even if that memory is not needed anymore? – Jim May 26 '15 at 18:05
  • @Jim It depends on the OS, unallocated memory may be *mapped* to a process, but if it's needed elsewhere then the OS unmapps it. – Some programmer dude May 27 '15 at 05:52
  • @JoachimPileborg:How come you don't post an answer? – Jim May 27 '15 at 07:07
  • @the8472 I followed your suggestion – Holger Jun 13 '18 at 10:37
  • @JoopEggen a VM [_does_ release memory to the OS under some conditions](https://stackoverflow.com/a/59377080/1059372) – Eugene Mar 18 '20 at 18:20
  • @Eugene appreciated. Did a half jvm myself. GC/JIT/... maybe is not a suited topic for a simple Q+A format. – Joop Eggen Mar 18 '20 at 21:58

6 Answers6

98

The HotSpot JVM does release memory back to the OS, but does so reluctantly since resizing the heap is expensive and it is assumed that if you needed that heap once you'll need it again.

In general shrinking ability and behavior depends on the chosen garbage collector, the JVM version since shrinking capability was often introduced in later versions long after the GC itself was added. Some collectors may also require additional options to be passed to opt into shrinking. And some most likely never will support it, e.g. EpsilonGC. So if heap shrinking is desired it should be tested for a particular JVM version and GC configuration.

JDK 8 and earlier

There are no explicit options for prompt memory reclamation in these versions but you can make the GC more aggressive in general by setting -XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30 which will allow it to spend more CPU time on collecting and constrain the amount of allocated-but-unused heap memory after a GC cycle.

If you're using a concurrent collector you can also set -XX:InitiatingHeapOccupancyPercent=N with N to some low value to let the GC run concurrent collections almost continuously, which will consume even more CPU cycles but shrink the heap sooner. This generally is not a good idea, but on some types of machines with lots of spare CPU cores but short on memory it can make sense.

If you're using G1GC note that it only gained the ability to yield back unused chunks in the middle of the heap with jdk8u20, earlier versions were only able to return chunks at the end of the heap which put significant limits on how much could be reclaimed.

If you're using a collector with a default pause time goal (e.g. CMS or G1) you can also relax that goal to place fewer constraints on the collector, or you can switch go the parallel collector to prioritize footprint over pause times.

To verify that shrinking occurs or to diagnose why a GC decides not to shrink you can use GC Logging with -XX:+PrintAdaptiveSizePolicy may also provide insight, e.g. when the JVM tries to use more memory for the young generation to meet some goals.

JDK 9

Added the -XX:-ShrinkHeapInSteps option which can be be used to apply the shrinking caused by the options mentioned in the previous section more aggressively. Relevant OpenJDK bug.

For logging -XX:+PrintAdaptiveSizePolicy has been replaced with -Xlog:gc+ergo

JDK 12

Introduced the option to enable prompt memory release for G1GC via the G1PeriodicGCInterval (JEP 346), again at the expense of some additional CPU. The JEP also mentions similar features in Shenandoah and the OpenJ9 VM.

JDK 13

Adds similar behavior for ZGC, in this case it is enabled by default. Additionally XXSoftMaxHeapSize can be helpful for some workloads to keep the average heap size below some threshold while still allowing transient spikes.

the8472
  • 40,999
  • 5
  • 70
  • 122
12

The JVM does release back memory under some circumstances, but (for performance reasons) this does not happen whenever some memory is garbage collected. It also depends on the JVM, OS, garbage collector etc. You can watch the memory consumption of your app with JConsole, VisualVM or another profiler.

Also see this related bug report

lbalazscs
  • 17,474
  • 7
  • 42
  • 50
  • 1
    I think what you see from these java memory tools is memory freed within the Java process space. But this does not mean that the OS gets the memory back and can give it to another application – Jim May 26 '15 at 13:04
  • 1
    You can also see the total heap size, but if you don't trust Java tools, you can also use native, OS-level tools. – lbalazscs May 26 '15 at 13:11
  • Correct me if I am wrong, but I can't really recall a case of a java process in e.g. the task manager ever shrinking. But I haven't really much experience in monitoring – Jim May 26 '15 at 17:54
  • 2
    I just verified to be sure, and I have seen it give back some memory! On Windows I like to use the vmmap app (it is a free download from Microsoft), it lets you see lots of interesting things about the memory usage of a process. – lbalazscs May 26 '15 at 20:29
9

If you use the G1 collector and call System.gc() occasionally (I do it once a minute), Java will reliably shrink the heap and give memory back to the OS.

Since Java 12, G1 does this automatically if the application is idle.

I recommend using these options combined with the above suggestion for a very compact resident process size:

-XX:+UseG1GC -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10

Been using these options daily for months with a big application (a whole Java-based guest OS) with dynamically loading and unloading classes - and the Java process almost always stays between 400 and 800 MB.

Stefan Reich
  • 1,000
  • 9
  • 12
  • What if `Xms` and `Xmx` are set to the same value. Will the G1 still give back memory to the OS? (Pre JDK 12) – Adwait Kumar Dec 17 '19 at 15:39
  • @AdwaitKumar No, then there is no wiggle room, obviously – Stefan Reich Dec 17 '19 at 21:46
  • 1
    @StefanReich I do not think that this is correct, _initalial heap_ and _max heap_ could be set to the same values for example, while the app could consume a lot less. – Eugene Dec 18 '19 at 12:17
  • The app can't consume less because Xms sets a minimum value. – Stefan Reich Dec 18 '19 at 13:54
  • 1
    @StefanReich 1) you have to tag me via `@`, otherwise I have no idea you replied 2) `Xms` is _not_ minimum memory, but _initial_, as such you are wrong. – Eugene Jan 12 '20 at 19:34
  • @Eugene If you were correct, Xms would mean almost nothing as the first GC cycle could release a lot of memory. Also I don't like to be called wrong, so I made a test program: http://code.botcompany.de/1026571. Running it as instructed, the heap grows to 1G once, then shrinks back to 512M after the first GC, consistent with the Xms value. Never shrinks below that. QED: Xms really is the minimum value. – Stefan Reich Jan 14 '20 at 12:37
  • @StefanReich your code does not open to begin with and second, it's your choice to believe what `Xms` stands for (that still makes you wrong, though). I don't even know what java version you used and that matters. – Eugene Jan 14 '20 at 14:22
  • @Eugene I'm still right. I used Java 13 of course. What does "code does not open" mean? I don't know if insults are allowed on SO but you really strike me as a confused individual. – Stefan Reich Jan 14 '20 at 23:17
  • @StefanReich my bad, I was not trying to insult you, in no shape or form, sorry if it sounded like that. Does not open means: I was trying to get that code about 9 hours ago, and it timed out. – Eugene Jan 15 '20 at 00:12
  • Stefan and @Eugene, `java -X` states *-Xms set initial Java heap size*. It's then up to the GC flavor to return memory back to the OS ... or not; and if it does it can decide to go below `-Xms` value ... or not. It's all up to it. But then, when used, we usually like to put the minimum value based on how the program is expected to work under its normal operation. – Matthieu Jan 30 '20 at 17:32
  • @Matthieu Fair enough. I was referring to the current JDK's actual behavior. I'd be surprised if there was any GC which behaved differently. But who knows. – Stefan Reich Jan 31 '20 at 13:30
  • @StefanReich [ZGC](https://openjdk.java.net/jeps/351) is supposed to do that, but it's not implemented. Until then, all GC implementations I heard of seem to be using `Xms` as a *minimum* size indeed. – Matthieu Jan 31 '20 at 15:39
  • @Eugene Then my webserver was at fault! Sorry. It think it cleared up too. I think makes more sense to keep Xms as a minimum value and it's good the VMs are like that. Changeable Xms and Xmx values would be a great improvement BTW. Can't somebody hack that into the JDK? Sounds very doable. – Stefan Reich Jan 31 '20 at 23:11
  • @StefanReich indeed, [it was a fun journey](https://stackoverflow.com/a/59377080/1059372). You were right, _under the current_ implementation it does not go below `Xms`. – Eugene Mar 18 '20 at 18:26
  • @Matthieu [I can only now say that I agree with you on the Xms value...](https://stackoverflow.com/a/59377080/1059372) – Eugene Mar 18 '20 at 18:27
  • 1
    @Eugene awesome analysis! So it seems the problem lies more in the documentation of `-Xms`, now that more and more people (including the ones *developing* GCs) say it *is* the minimum heap value? – Matthieu Mar 18 '20 at 22:39
  • 1
    @Matthieu especially since there is `InitialHeapSize` already, making this even more confusing. – Eugene Mar 19 '20 at 01:06
3

this article here explains how the GC work in Java 7. In a nutshell, there are many different garbage collectors available. Usually the memory is kept for the Java process and only some GCs release it to the system (upon request I think). But, the memory used by the Java process will not grow indefinitely, as there is an upper limit defined by the Xmx option (which is 256m usually, but I think it is OS/machine dependant).

francesco foresti
  • 2,004
  • 20
  • 24
  • 1
    The OP is not update if heap has an upper limit but if the unused part of the heap is eventually reclaimed by OS to be used by other processes – Jim May 26 '15 at 17:53
  • I think what Francesco Foresti is saying is that some garbage collection settings will result in the heap shrinking and memory being given back to the OS, which I have seen in practice, also, see for instance: [link](http://www.stefankrause.net/wp/?p=14) – nsandersen Dec 09 '16 at 18:01
1

ZGC released in 13 java and it can return unused heap memory to the operating system Please see the link

  • 1
    Link-only answers are discouraged because links may break over time. Please extract relevant information from the post and add them to your answer, either as a quotation or in your own words. – Genhis Jul 26 '19 at 11:53
  • 3
    @Genhis OTOH the links provided in this specific answer should be "quite" stable, since they point to JEPs on java.net. – Flávio Etrusco Oct 24 '19 at 20:21
  • 1
    ZGC was experimental in JDK 11. it is still experimental. – Nitsan Wakart Dec 13 '19 at 13:46
0

Does GC release back memory to OS?

Short Answer: The memory is returned back to the operating system immediately or at a later time depends upon the configuration (algorithm) of the garbage collector.

JDK 1.8

If you are using JDK 8 or earlier and run below code snippet:

public static void main(String[] args) throws InterruptedException {
    Runtime runtime = Runtime.getRuntime();
    long availableMemory = runtime.freeMemory();
    System.out.println("Initially Available Memory : " + availableMemory);

    List<Customer> customers = new ArrayList<>();
    for (int loop = 0; loop < 1_00_000; loop++) {
        customers.add(new Customer("USERNAME"));
    }
    availableMemory = runtime.freeMemory();
    System.out.println("Post Processing Available Memory : " + availableMemory);

    customers= new ArrayList<>();
    Thread.sleep(1000);
    System.gc();

    availableMemory = runtime.freeMemory();
    System.out.println("Post Garbage Collecting Available Memory : " + availableMemory);
}

Using JDK 1.8 it results in the below statistics on my machine:

Initially Available Memory :                   243620776 
Post Processing Memory :                       240444976 
Post Garbage Collecting Memory :               250960720 

Results: In the case of JDK 1.8, when we call the System.gc() we get the free memory back and JVM receives it and doesn't return it to the OS.

JDK 11+

When running the same above code snippet, we yield a surprising result:

Initially Available Memory:                   242021048 
Post Processing Memory:                       239171744 
Post Garbage Collecting Memory:                61171920 

Results: In the case of JDK 11+, when we call the System.gc() JVM frees the memory and returns it to the OS.

Consequences:

Whenever we return the free memory to the Operating System, if we require more memory we have to request to the OS for a free chunk of additional memory. The overhead results in slow performance.

Request JDK 11+ not to release free memory back to the OS.

We can set the initial heap size to high, that way we can simply ensure that we never face such a situation. We can set the initial heap size high by -Xms.

$ java -Xms2g MyProgram.java
Initially Available Memory :                  2124414976
Post Processing Memory :                      2120842392
Post Garbage Collecting Memory :              2144444600

I have used ThinkPad 16,082 MB RAM Available Physical Memory 4,340 MB Virtual Memory: Max Size: 21,202 MB Virtual Memory: Available: 6,123 MB Virtual Memory: In Use: 15,079 MB

Zahid Khan
  • 2,130
  • 2
  • 18
  • 31