--- old/src/java.base/share/classes/java/lang/ref/Reference.java 2016-04-01 16:47:26.466820776 +0200 +++ new/src/java.base/share/classes/java/lang/ref/Reference.java 2016-04-01 16:47:26.351820046 +0200 @@ -25,11 +25,10 @@ package java.lang.ref; -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; +import jdk.internal.vm.annotation.DontInline; /** * 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,16 @@ * 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; + + /* Discovery phase counter, guarder by above lock. + */ + private static int discoveryPhase; + + /* Enqueue phase, guarded by enqueuePhaseLock object. + */ + private static int enqueuePhase; + private static final Object enqueuePhaseLock = new Object(); /* High-priority thread to enqueue pending References */ @@ -139,11 +147,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) { @@ -151,73 +158,118 @@ } public void run() { + int[] discoveryPhase = new int[1]; while (true) { - tryHandlePending(true); + Reference p = getPendingReferences(discoveryPhase); + enqueuePendingReferences(p, discoveryPhase[0]); } } } /** - * 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, sets the 0-th element + * of given {@code discoveryPhaseHolder} to the phase in which they were + * discovered and returns the head of the list of discovered references. + * + * @param discoveryPhaseHolder a 1-element array to hold the returned + * discovery phase. + * @return the head of a list of pending references linked via + * {@link #discovered} field with {@code null} marking the end of list. + */ + static Reference getPendingReferences(int[] discoveryPhaseHolder) { + 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; + // increment discoveryPhase counter and return it in a holder array + discoveryPhaseHolder[0] = ++discoveryPhase; + } + return p; + } + + /** + * @return current finished discovery phase. + */ + static int getDiscoveryPhase() { + synchronized (lock) { + return (pending == null) + ? discoveryPhase // already incremented + : discoveryPhase + 1; // not yet incremented + } + } + + /** + * Enqueue a list of pending {@link Reference}s linked via {@link #discovered} + * field with {@code null} marking the end of list. + *

+ * The {@link #enqueuePhase} is set to given {@code discoveryPhase} + * after all references from the list have been enqueued and any waiters on + * {@link #enqueuePhaseLock} are notified. + * + * @param p a list of pending references linked via {@link #discovered} + * field with {@code null} marking the end of list. + * @param discoveryPhase the phase in which given references were discovered. + */ + static void enqueuePendingReferences(Reference p, int discoveryPhase) { + try { + // distribute unhooked pending references to their respective queues + 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); + } + } finally { + // mark the enqueueing of references discovered in given + // discovery phase is finished and notify waiters. + synchronized (enqueuePhaseLock) { + enqueuePhase = discoveryPhase; + enqueuePhaseLock.notifyAll(); + } + } + } + + /** + * Triggers discovery of new Reference(s) and returns the phase sequence number + * in which they were discovered or previous phase sequence number if no new + * Reference(s) were discovered. + */ + static int discoverReferences() { + // trigger discovery of new Reference(s) + System.gc(); + // obtain the phase in which they were discovered (if any) + return getDiscoveryPhase(); + } + + /** + * Blocks until all Reference(s) that were discovered in given + * {@code discoveryPhase} (as returned by {@link #discoverReferences()}) + * have been enqueued. + * + * @param discoveryPhase the discovery phase sequence number. + * @throws InterruptedException if interrupted while waiting. + */ + static void awaitReferencesEnqueued(int discoveryPhase) throws InterruptedException { + // await for them to be enqueued + synchronized (enqueuePhaseLock) { + while (enqueuePhase - discoveryPhase < 0) { + enqueuePhaseLock.wait(); + } + } } static { @@ -236,8 +288,14 @@ // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override - public boolean tryHandlePendingReference() { - return tryHandlePending(false); + public int discoverReferences() { + return Reference.discoverReferences(); + } + + @Override + public void awaitReferencesEnqueued( + int discoveryPhase) throws InterruptedException { + Reference.awaitReferencesEnqueued(discoveryPhase); } }); }