< prev index next >
src/java.base/share/classes/java/lang/ref/Reference.java
Print this page
*** 27,37 ****
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
--- 27,36 ----
*** 124,133 ****
--- 123,137 ----
* 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,225 ****
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
! // pre-load and initialize InterruptedException and Cleaner classes
// so that we don't get into trouble later in the run loop if there's
! // memory shortage while loading/initializing them 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);
}
}
}
/**
! * 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;
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<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
! return true;
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
--- 141,247 ----
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
! // 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 it lazily.
ensureClassInitialized(InterruptedException.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, null, name, 0, false);
}
public void run() {
while (true) {
! enqueuePendingReferences(true);
}
}
}
/**
! * 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) {
! while ((p = pending) == null && waitForNotifyFromGc) {
lock.wait();
}
! // 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) {
! // 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();
! return;
} catch (InterruptedException x) {
! 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);
! }
! } 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,245 ****
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
! public boolean tryHandlePendingReference() {
! return tryHandlePending(false);
}
});
}
/* -- Referent accessor and setters -- */
--- 256,272 ----
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
! public void enqueuePendingReferences() {
! Reference.enqueuePendingReferences(false);
! }
!
! @Override
! public boolean cleanNextEnqueuedCleanable(Cleaner cleaner) {
! return cleaner.cleanNextEnqueued();
}
});
}
/* -- Referent accessor and setters -- */
< prev index next >