0

With the System.gc() method it is possible to force a full GC run. This is very expensive. Is there an option to force the first generation GC only? My application is currently running on Java 11. I am thinking of something like MemoryPoolMXBean.

Background: I want to start a large memory consuming task and if the free memory is lower than the needed memory then I want to know if I can start it without risk or should I try it later or swap cache to the disk. In real I want to know the free memory after such first GC. Our analysis shows that this will be enough in most cases (>99%).

Lebecca
  • 2,406
  • 15
  • 32
Horcrux7
  • 23,758
  • 21
  • 98
  • 156
  • No, that is not possible. Also, the call to `System.gc()`is just a suggestion to the JVM that a gc should be performed although, in practice, I'm not aware of any JVM not running the gc on a `System.gc()`call. – Erik Jan 09 '21 at 21:42
  • @Erik but that is a concurrent mark phase that is triggered, it might yield exactly zero _actual_ garbage reclamation. – Eugene Jan 10 '21 at 19:17
  • @Eugene I considered that but that case is more of a wish than something you could rely on so it didn't seem worth a mention. However, I do wonder abit about the described use case. How large a heap are we talking about here if the gc time for it can be a problem considering the stated background? – Erik Jan 10 '21 at 20:42
  • Because we work with memory consuming operation the heap can be very large. Many GB. Then the GC can need more as a second. It is also a question of frequency of such a check. – Horcrux7 Jan 11 '21 at 12:41

2 Answers2

1

The short answer is that there is no supported way to do this. It is System.gc() or nothing.

Longer answer:

I trawled the JVM source code and found the C++ CollectedHeap API. (It is in "src/hotspot/share/gc/shared/collectedHeap.hpp" if you are interested.) This is the internal interface between the JVM's configured heap and the rest of the JVM.

It you look at this API, you can see the virtual methods that the JVM can call to trigger a garbage collection. It turns out that there are three of them, but they all invoke a full garbage collection. So even if you were to try to trigger a GC from native code invoked using JNI, you would stull end up doing a full GC.

It would be possible to download the OpenJDK source code, modify the CollectedHeap API and the heap implementations so that you could trigger a new generation GC, and build a custom JVM with the functionality that you require. You could even distribute the binaries provided that you abided by the terms of the OpenJDK "GPLv2 + Classpath exception" license. However, that would be a huge undertaking.


But I think your question is misguided.

Triggering a GC is almost always a bad idea. As a general rule, the heap itself knows the best (most efficient) time to run a GC in order to meet the goals that you have configured in the GC tuning options. The best strategy is to not interfere. Just let the JVM take care of the timing.

The fundamental issue is that most of the cost of a gc() run is taken in dealing with the non-garbage objects; i.e. the objects that need to be copied from one space to another by the collector. Any garbage that is left behind will (typically) just be zeroed. So the most ergonomic time to run a collection is when the ratio of garbage to non-garbage (in the space being collected) is as high as possible; i.e. when it is close to full.

The only plausibly good reason to interfere (by calling System.gc()) in production code is if the application has points where it knows that it is OK to be inefficient. A classic example is a game where GC pauses are harmful during normal play, but they are OK when (for example) loading the next "level". So triggering the GC after loading a level could be beneficial.

It is not clear from your question and comments what you are aiming to achieve by forcing the GC to run. But my advice is to read up on Java GC tuning, set the JVM options, and then leave it to the JVM to manage the heap.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks. The problem, if I stop all until a GC was run then I can wait for days without a GC ever occur. If I continue in a low memory situation then it will result in an OutOfMemoryError which can have fatal results for the server self. – Horcrux7 Jan 22 '21 at 12:34
  • OK. So that is an issue of your choice of garbage collector. With the most recent versions of Java, there are collectors that are designed to perform well with extremely large heaps. Look at the Z and Shenandoah collectors; https://medium.com/@hasithalgamge/seven-types-of-java-garbage-collectors-6297a1418e82 – Stephen C Jan 23 '21 at 02:05
0

I use the follow code to check if there are enough memory for the memory intentive operation:

int requiredMemory = ...;
// force a first generation GC
try {
    byte[] ram = new byte[requiredMemory];
    requiredMemory = ram[0]; // should be ever 0 and prevent optimize through JIT because the allocated array works outside of the block
} catch( OutOfMemoryError ex ) {
   // ignore, uncritical because it occur through a large block
}
if( requiredMemory == 0 ) {
   ...
}
Horcrux7
  • 23,758
  • 21
  • 98
  • 156