< prev index next >
src/java.base/share/classes/java/nio/Bits.java
Print this page
@@ -24,16 +24,18 @@
*/
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.
*/
@@ -601,15 +603,11 @@
private static final AtomicLong reservedMemory = new AtomicLong();
private static final AtomicLong totalCapacity = new AtomicLong();
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
// which a process may access. All sizes are specified in bytes.
static void reserveMemory(long size, int cap) {
@@ -622,56 +620,43 @@
// optimist!
if (tryReserveMemory(size, cap)) {
return;
}
- final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
+ // reservation threads that don't succeed at first must queue so that
+ // some of them don't starve while others succeed.
+ reservationQueue.lock();
+ try {
+ 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()) {
+ // 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()));
- // trigger VM's Reference processing
+ // trigger GC's Reference discovery
System.gc();
+ // make sure all newly discovered Reference(s) are enqueued
+ jlra.enqueuePendingReferences();
- // a retry loop with exponential back-off delays
- // (this gives VM some time to do it's job)
- boolean interrupted = false;
- try {
- long sleepTime = 1;
- int sleeps = 0;
- while (true) {
+ // 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()));
// no luck
throw new OutOfMemoryError("Direct buffer memory");
-
} finally {
- if (interrupted) {
- // don't swallow interrupts
- Thread.currentThread().interrupt();
- }
+ reservationQueue.unlock();
}
}
private static boolean tryReserveMemory(long size, int cap) {
< prev index next >