< prev index next >
src/java.base/share/classes/java/lang/ref/Reference.java
Print this page
*** 23,37 ****
* questions.
*/
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;
/**
* 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
--- 23,36 ----
* questions.
*/
package java.lang.ref;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.JavaLangRefAccess;
import jdk.internal.misc.SharedSecrets;
! import jdk.internal.vm.annotation.DontInline;
/**
* 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
*** 105,132 ****
/* When active: next element in a discovered reference list maintained by GC (or this if last)
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*/
! private transient Reference<T> discovered; /* used by VM */
/* Object used to synchronize with the garbage collector. The collector
* must acquire this lock at the beginning of each collection cycle. It is
* therefore critical that any code holding this lock complete as quickly
* as possible, allocate no new objects, and avoid calling user code.
*/
private static class Lock { }
! private static Lock lock = new Lock();
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* 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;
/* High-priority thread to enqueue pending References
*/
private static class ReferenceHandler extends Thread {
--- 104,140 ----
/* When active: next element in a discovered reference list maintained by GC (or this if last)
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*/
! private transient Reference<?> discovered; /* used by VM */
/* Object used to synchronize with the garbage collector. The collector
* must acquire this lock at the beginning of each collection cycle. It is
* therefore critical that any code holding this lock complete as quickly
* as possible, allocate no new objects, and avoid calling user code.
*/
private static class Lock { }
! private static final Lock lock = new Lock();
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* them. This list is protected by the above lock object. The
* list uses the discovered field to link its elements.
*/
! 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
*/
private static class ReferenceHandler extends Thread {
*** 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;
--- 145,277 ----
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() {
+ int[] discoveryPhase = new int[1];
while (true) {
! Reference<?> p = getPendingReferences(discoveryPhase);
! enqueuePendingReferences(p, discoveryPhase[0]);
}
}
}
/**
! * 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 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
! }
! }
! 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.
! * <p>
! * 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<Object> 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 {
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 -- */
--- 286,303 ----
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
! public int discoverReferences() {
! return Reference.discoverReferences();
! }
!
! @Override
! public void awaitReferencesEnqueued(
! int discoveryPhase) throws InterruptedException {
! Reference.awaitReferencesEnqueued(discoveryPhase);
}
});
}
/* -- Referent accessor and setters -- */
< prev index next >