< 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 cleanNextEnqueuedCleanable(Cleaner cleaner) {
+ return cleaner.cleanNextEnqueued();
}
});
}
/* -- Referent accessor and setters -- */
< prev index next >