--- old/src/java.base/share/classes/java/nio/Bits.java 2016-03-06 13:37:17.609644563 +0100 +++ new/src/java.base/share/classes/java/nio/Bits.java 2016-03-06 13:37:17.511646251 +0100 @@ -26,12 +26,14 @@ package java.nio; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantLock; import jdk.internal.misc.JavaNioAccess; import jdk.internal.misc.JavaLangRefAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; +import jdk.internal.ref.CleanerFactory; /** * Access to bits, native and otherwise. @@ -603,11 +605,7 @@ private static final AtomicLong count = new AtomicLong(); private static volatile boolean memoryLimitSet; - // max. number of sleeps during try-reserving with exponentially - // increasing delay before throwing OutOfMemoryError: - // 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s) - // which means that OOME will be thrown after 0.5 s of trying - private static final int MAX_SLEEPS = 9; + private static final ReentrantLock reservationQueue = new ReentrantLock(true); // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory @@ -624,52 +622,39 @@ return; } - final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); - - // retry while helping enqueue pending Reference objects - // which includes executing pending Cleaner(s) which includes - // Cleaner(s) that free direct buffer memory - while (jlra.tryHandlePendingReference()) { - if (tryReserveMemory(size, cap)) { - return; - } - } - - // trigger VM's Reference processing - System.gc(); - - // a retry loop with exponential back-off delays - // (this gives VM some time to do it's job) - boolean interrupted = false; + // reservation threads that don't succeed at first must queue so that + // some of them don't starve while others succeed. + reservationQueue.lock(); try { - long sleepTime = 1; - int sleeps = 0; - while (true) { + JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); + + // retry reservation while helping the Cleaner thread process enqueued + // Cleanable(s) which includes the ones that free direct buffer memory + // until the queue drains out + do { if (tryReserveMemory(size, cap)) { return; } - if (sleeps >= MAX_SLEEPS) { - break; - } - if (!jlra.tryHandlePendingReference()) { - try { - Thread.sleep(sleepTime); - sleepTime <<= 1; - sleeps++; - } catch (InterruptedException e) { - interrupted = true; - } + } while (jlra.cleanNextEnqueuedCleanable(CleanerFactory.cleaner())); + + // trigger GC's Reference discovery + System.gc(); + // make sure all newly discovered Reference(s) are enqueued + jlra.enqueuePendingReferences(); + + // retry reservation while helping the Cleaner thread process enqueued + // Cleanable(s) which includes the ones that free direct buffer memory + // until the queue drains out + do { + if (tryReserveMemory(size, cap)) { + return; } - } + } while (jlra.cleanNextEnqueuedCleanable(CleanerFactory.cleaner())); // no luck throw new OutOfMemoryError("Direct buffer memory"); - } finally { - if (interrupted) { - // don't swallow interrupts - Thread.currentThread().interrupt(); - } + reservationQueue.unlock(); } }