< prev index next >

src/java.base/share/classes/java/lang/ref/Reference.java

Print this page

        

@@ -27,11 +27,10 @@
 
 import jdk.internal.vm.annotation.DontInline;
 import jdk.internal.HotSpotIntrinsicCandidate;
 import jdk.internal.misc.JavaLangRefAccess;
 import jdk.internal.misc.SharedSecrets;
-import jdk.internal.ref.Cleaner;
 
 /**
  * Abstract base class for reference objects.  This class defines the
  * operations common to all reference objects.  Because reference objects are
  * implemented in close cooperation with the garbage collector, this class may

@@ -124,10 +123,15 @@
      * them.  This list is protected by the above lock object. The
      * list uses the discovered field to link its elements.
      */
     private static Reference<Object> pending = null;
 
+    /* Phase counters. Guarded by the above lock object.
+     */
+    private static int unhookPhase;
+    private static int enqueuePhase;
+
     /* High-priority thread to enqueue pending References
      */
     private static class ReferenceHandler extends Thread {
 
         private static void ensureClassInitialized(Class<?> clazz) {

@@ -137,89 +141,107 @@
                 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
             }
         }
 
         static {
-            // pre-load and initialize InterruptedException and Cleaner classes
+            // pre-load and initialize InterruptedException class
             // so that we don't get into trouble later in the run loop if there's
-            // memory shortage while loading/initializing them lazily.
+            // memory shortage while loading/initializing it lazily.
             ensureClassInitialized(InterruptedException.class);
-            ensureClassInitialized(Cleaner.class);
         }
 
         ReferenceHandler(ThreadGroup g, String name) {
             super(g, null, name, 0, false);
         }
 
         public void run() {
             while (true) {
-                tryHandlePending(true);
+                enqueuePendingReferences(true);
             }
         }
     }
 
     /**
-     * Try handle pending {@link Reference} if there is one.<p>
-     * Return {@code true} as a hint that there might be another
-     * {@link Reference} pending or {@code false} when there are no more pending
-     * {@link Reference}s at the moment and the program can do some other
-     * useful work instead of looping.
-     *
-     * @param waitForNotify if {@code true} and there was no pending
-     *                      {@link Reference}, wait until notified from VM
-     *                      or interrupted; if {@code false}, return immediately
-     *                      when there is no pending {@link Reference}.
-     * @return {@code true} if there was a {@link Reference} pending and it
-     *         was processed, or we waited for notification and either got it
-     *         or thread was interrupted before being notified;
-     *         {@code false} otherwise.
-     */
-    static boolean tryHandlePending(boolean waitForNotify) {
-        Reference<Object> r;
-        Cleaner c;
+     * Enqueue pending {@link Reference}s if GC has discovered any.<p>
+     * This method does not return until all references that had been discovered
+     * by the time this method was called, are enqueued even if some of them are
+     * being enqueued concurrently by other threads.
+     *
+     * @param waitForNotifyFromGc if {@code true} and there is no pending
+     *                            {@link Reference}, wait until VM discovers some
+     *                            or interrupted (in which case the interrupted
+     *                            status is cleared); if {@code false} and there
+     *                            is no pending reference, just wait until threads
+     *                            that found some discovered references before
+     *                            us enqueue them all.
+     */
+    static void enqueuePendingReferences(boolean waitForNotifyFromGc) {
+        Reference<Object> p;
+        int unhookedInPhase;
         try {
             synchronized (lock) {
-                if (pending != null) {
-                    r = pending;
-                    // 'instanceof' might throw OutOfMemoryError sometimes
-                    // so do this before un-linking 'r' from the 'pending' chain...
-                    c = r instanceof Cleaner ? (Cleaner) r : null;
-                    // unlink 'r' from 'pending' chain
-                    pending = r.discovered;
-                    r.discovered = null;
-                } else {
-                    // The waiting on the lock may cause an OutOfMemoryError
-                    // because it may try to allocate exception objects.
-                    if (waitForNotify) {
+                while ((p = pending) == null && waitForNotifyFromGc) {
                         lock.wait();
                     }
-                    // retry if waited
-                    return waitForNotify;
-                }
+                // unhook the whole chain of pending References at once
+                pending = null;
+                // remember the phase in which we unhooked a chain
+                // of pending references and increment the counter
+                unhookedInPhase = unhookPhase++;
             }
         } catch (OutOfMemoryError x) {
-            // Give other threads CPU time so they hopefully drop some live references
-            // and GC reclaims some space.
-            // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
-            // persistently throws OOME for some time...
+            // The waiting on the lock may cause an OutOfMemoryError
+            // because it may try to allocate InterruptedException object.
+            // Give other threads CPU time so they hopefully drop some live
+            // references and GC reclaims some space.
             Thread.yield();
-            // retry
-            return true;
+            return;
         } catch (InterruptedException x) {
-            // retry
-            return true;
-        }
-
-        // Fast path for cleaners
-        if (c != null) {
-            c.clean();
-            return true;
+            return;
         }
 
+        try {
+            // distribute unhooked pending references to their respective queues
+            while (p != null) {
+                Reference<Object> r = p;
+                p = r.discovered;
+                r.discovered = null;
         ReferenceQueue<? super Object> q = r.queue;
         if (q != ReferenceQueue.NULL) q.enqueue(r);
-        return true;
+            }
+        } finally {
+            boolean interrupted = false;
+            synchronized (lock) {
+                // wait for concurrent enqueuing threads that unhooked
+                // pending references before us to finish with enqueueing
+                // before proceeding...
+                try {
+                    while (unhookedInPhase - enqueuePhase > 0) {
+                        try {
+                            lock.wait();
+                        } catch (OutOfMemoryError e) {
+                            // The waiting on the lock may cause an OutOfMemoryError
+                            // (we swallow such interrupt as we are not sure about
+                            // OOME cause).
+                            Thread.yield();
+                        } catch (InterruptedException e) {
+                            // remember that we were interrupted
+                            interrupted = true;
+                        }
+                    }
+                } finally {
+                    // increment enqueue phase counter
+                    enqueuePhase++;
+                    // notify waiters
+                    lock.notifyAll();
+                }
+            }
+            // re-assert thread interrupted status
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
     }
 
     static {
         ThreadGroup tg = Thread.currentThread().getThreadGroup();
         for (ThreadGroup tgn = tg;

@@ -234,12 +256,17 @@
         handler.start();
 
         // provide access in SharedSecrets
         SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
             @Override
-            public boolean tryHandlePendingReference() {
-                return tryHandlePending(false);
+            public void enqueuePendingReferences() {
+                Reference.enqueuePendingReferences(false);
+            }
+
+            @Override
+            public boolean cleanNextPendingCleanable(Cleaner cleaner) {
+                return cleaner.cleanNextPending();
             }
         });
     }
 
     /* -- Referent accessor and setters -- */
< prev index next >