diff -r 0688ba09248f -r e2c92ddeb57f jdk/src/share/classes/java/lang/ref/Reference.java --- a/jdk/src/share/classes/java/lang/ref/Reference.java Mon Feb 24 10:34:07 2014 +0100 +++ b/jdk/src/share/classes/java/lang/ref/Reference.java Mon Feb 24 15:34:33 2014 +0100 @@ -26,6 +26,8 @@ package java.lang.ref; import sun.misc.Cleaner; +import sun.misc.JavaLangRefAccess; +import sun.misc.SharedSecrets; /** * Abstract base class for reference objects. This class defines the @@ -147,51 +149,75 @@ } public void run() { - for (;;) { - Reference r; - Cleaner c; - 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. - lock.wait(); - continue; - } - } - } 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... - Thread.yield(); - // retry - continue; - } catch (InterruptedException x) { - // retry - continue; - } - - // Fast path for cleaners - if (c != null) { - c.clean(); - continue; - } - - ReferenceQueue q = r.queue; - if (q != ReferenceQueue.NULL) q.enqueue(r); + while (true) { + tryHandlePending(true); } } } + /** + * Try handle pending {@link Reference} if there is one.

+ * 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 r; + Cleaner c; + 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) { + lock.wait(); + } + // retry if waited + return waitForNotify; + } + } + } 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... + Thread.yield(); + // retry + return true; + } catch (InterruptedException x) { + // retry + return true; + } + + // Fast path for cleaners + if (c != null) { + c.clean(); + return true; + } + + ReferenceQueue q = r.queue; + if (q != ReferenceQueue.NULL) q.enqueue(r); + return true; + } + static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; @@ -204,9 +230,16 @@ handler.setPriority(Thread.MAX_PRIORITY); handler.setDaemon(true); handler.start(); + + // provide access in SharedSecrets + SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { + @Override + public boolean tryHandlePendingReference() { + return tryHandlePending(false); + } + }); } - /* -- Referent accessor and setters -- */ /**