--- old/src/java.base/share/classes/java/lang/ref/Reference.java 2016-03-24 16:33:10.252497507 +0100 +++ new/src/java.base/share/classes/java/lang/ref/Reference.java 2016-03-24 16:33:10.164497045 +0100 @@ -29,7 +29,6 @@ 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 @@ -107,7 +106,7 @@ * pending: next element in the pending list (or null if last) * otherwise: NULL */ - private transient Reference discovered; /* used by VM */ + private transient Reference discovered; /* used by VM */ /* Object used to synchronize with the garbage collector. The collector @@ -116,7 +115,7 @@ * as possible, allocate no new objects, and avoid calling user code. */ private static class Lock { } - private static Lock lock = new Lock(); + private static final Lock lock = new Lock(); /* List of References waiting to be enqueued. The collector adds @@ -124,7 +123,7 @@ * them. This list is protected by the above lock object. The * list uses the discovered field to link its elements. */ - private static Reference pending = null; + private static Reference pending; /* High-priority thread to enqueue pending References */ @@ -139,11 +138,10 @@ } 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) { @@ -152,72 +150,54 @@ public void run() { while (true) { - tryHandlePending(true); + Reference p = getPendingReferences(); + enqueuePendingReferences(p); } } } /** - * 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 { + * Blocks until GC discovers some pending references and hands them to us. + * + * @return a list of pending references linked via {@link #discovered} field + * with {@code null} marking the end of list. + */ + static Reference getPendingReferences() { + Reference p; + synchronized (lock) { + while ((p = pending) == null) { + try { + lock.wait(); + } catch (OutOfMemoryError x) { // 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; + // 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(); + } catch (InterruptedException x) { + // ignore interrupts } } - } 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; + pending = null; + } + return p; + } + + /** + * Enqueue a list of pending {@link Reference}s. + * + * @param p a list of pending references linked via {@link #discovered} + * field with {@code null} marking the end of list + */ + static void enqueuePendingReferences(Reference p) { + while (p != null) { + Reference r = p; + p = r.discovered; + r.discovered = null; + @SuppressWarnings("unchecked") + ReferenceQueue q = (ReferenceQueue) r.queue; + if (q != ReferenceQueue.NULL) q.enqueue(r); + } } static { @@ -235,9 +215,10 @@ // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { + @Override - public boolean tryHandlePendingReference() { - return tryHandlePending(false); + public boolean cleanNextEnqueuedCleanable(Cleaner cleaner) { + return cleaner.cleanNextEnqueued(); } }); }