Skip to content
Advertisement

Is the System.gc() call in sun.nio.ch.FileChannelImpl a bad case?

try {
    // If no exception was thrown from map0, the address is valid
    addr = map0(imode, mapPosition, mapSize);
} catch (OutOfMemoryError x) {
    // An OutOfMemoryError may indicate that we've exhausted memory
    // so force gc and re-attempt map
    System.gc();
    try {
        Thread.sleep(100);
    } catch (InterruptedException y) {
        Thread.currentThread().interrupt();
    }
    try {
        addr = map0(imode, mapPosition, mapSize);
    } catch (OutOfMemoryError y) {
        // After a second OOME, fail
        throw new IOException("Map failed", y);
    }
}

From jdk/FileChannelImpl.java at jdk8-b120.

Does this help with exception recovery?

Advertisement

Answer

When an object allocation fails with an OutOfMemoryError, the garbage collector did already try its best to reclaim memory of unused objects or expanding the heap. So calling System.gc() after catching OutOfMemoryError would be pointless.

A special case is the situation when the garbage collector repeatedly did reclaim a tiny amount of memory, so the application could proceed, but have to perform another garbage collection right on the next allocation. Some garbage collectors throw an OutOfMemoryError with the message “GC Overhead Limit Exceeded” when they detect that the application spent more than 98% of the CPU time for garbage collection. In this situation, calling System.gc() would be counter productive, as then, the application spends even more time in garbage collection plus creating and processing OutOfMemoryErrors.


The FileChannelImpl’s case is different. map0 may fail due to insufficient native memory or, in case of 32 bit systems, when running out of address space. In these cases, the heap memory manager did not produce the OutOfMemoryError and it is possible that the garbage collector didn’t run. But to reclaim native memory or address space, the associated ByteBuffer instances must get garbage collected, so their cleaner can run. This is a rare corner case where calling System.gc(); makes sense.

It’s still fragile, as System.gc(); is not guaranteed to collect all objects or to run the garbage collector at all. JEP 383 is supposed to solve this, by providing better control over the lifetime of native allocations.

Advertisement