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 OutOfMemoryError
s.
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.