Search code examples
jvmgarbage-collectionjvm-hotspot

Why do we need Thread.sleep after calling System.gc in JDK native memory usage scenario?


After my research on the source code implementation details of System.gc in Java17 ,i found that System.gcwill eventually trigger two gc types:

  1. full gc (stop the world), when we use SerialGC, ParallelGC, ZGC. and the java thread which invoked the System.gc will be blocked until the full gc finished.

  2. concurrent full gc, when we use G1, Shenandoah with -XX:+ExplicitGCInvokesConcurrent, and the java thread will return from the System.gc soon after trigger concurrent full gc.

My question

now we discuss the case of full gc ,since java thread will always block in native code(safe point) before full gc is completed,, why does jdk call Thread.sleep after calling System.gc ?

do We really need Thread.sleep here ?

public class FileChannelImpl extends FileChannel
{
   private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync)
                              throws IOException
   {
            try {
                    // If map0 did not throw an exception, the address is valid
                    addr = map0(prot, mapPosition, mapSize, isSync);
                } catch (OutOfMemoryError x) {
                    // An OutOfMemoryError may indicate that we've exhausted
                    // memory so force gc and re-attempt map
                    System.gc();
                    try {
                        // do We really need Thread.sleep here ?
                        Thread.sleep(100);
                    } catch (InterruptedException y) {
                        Thread.currentThread().interrupt();
                    }
                    try {
                        addr = map0(prot, mapPosition, mapSize, isSync);
                    } catch (OutOfMemoryError y) {
                        // After a second OOME, fail
                        throw new IOException("Map failed", y);
                    }
              }

   }
}

The java thread will not return from System.gc until the full gc is completed,So I think there is no need to call Thread.sleep after calling System.gc( here


Solution

  • Your statement “java thread will return from the System.gc soon after trigger concurrent full gc” contradicts your assumption “java thread will not return from System.gc until the full gc is completed”.

    Triggering a concurrent garbage collection does not imply waiting for its completion.

    But regardless of whether the garbage collection completes before returning from System.gc() or not, the operation in question is not waiting for the completion of the garbage collection, its waiting for the completion of the cleaner which may free existing native memory allocations.

    This internal cleaner works similar to the one that is nowadays publicly available. It runs in a different thread than the garbage collector and waits for the garbage collector to hand over discovered phantom references whose referent became unreachable.

    So its crucial to give the cleaner thread enough time, to potentially change from waiting state to running state and execute the cleaning action, i.e. deallocate native memory (or release file mappings).