< 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.StampedLock;
 
 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,18 @@
     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;
+    // A fair lock for direct memory allocator threads to queue after 1st optimistic
+    // reservation fails so that only a single thread at a time is retrying
+    // reservation while:
+    //   - helping out the Cleaner thread process enqueued Cleanable(s)
+    //   - followed by triggering reference discovery and waiting for references to be enqueued
+    //   - followed by another round of helping the Cleaner thread
+    // ... before finally giving up with OutOfMemoryError.
+    private static final StampedLock reservationLock = new StampedLock();
 
     // 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) {

@@ -618,58 +623,58 @@
             maxMemory = VM.maxDirectMemory();
             memoryLimitSet = true;
         }
 
         // optimist!
+        long stamp = reservationLock.tryReadLock();
+        if (stamp != 0L) try {
         if (tryReserveMemory(size, cap)) {
             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;
-            }
+        } finally {
+            reservationLock.unlockRead(stamp);
         }
 
-        // 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)
+        // reservation threads that don't succeed at first must queue so that
+        // some of them don't starve while others succeed.
         boolean interrupted = false;
+        stamp = reservationLock.writeLock();
         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()) {
+            } while (jlra.cleanNextEnqueuedCleanable(CleanerFactory.cleaner()));
+
+            // trigger Reference discovery and wait until discovered Reference(s)
+            // have been enqueued...
                     try {
-                        Thread.sleep(sleepTime);
-                        sleepTime <<= 1;
-                        sleeps++;
+                jlra.discoverAndEnqueueReferences();
                     } catch (InterruptedException e) {
+                // don't swallow interrupts
                         interrupted = 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;
                 }
-            }
+            } while (jlra.cleanNextEnqueuedCleanable(CleanerFactory.cleaner()));
 
             // no luck
             throw new OutOfMemoryError("Direct buffer memory");
-
         } finally {
+            reservationLock.unlockWrite(stamp);
             if (interrupted) {
-                // don't swallow interrupts
                 Thread.currentThread().interrupt();
             }
         }
     }
 
< prev index next >