< prev index next >
src/java.base/share/classes/java/lang/ref/Reference.java
Print this page
*** 25,36 ****
package java.lang.ref;
import sun.misc.Cleaner;
import sun.misc.JavaLangRefAccess;
- import sun.misc.ManagedLocalsThread;
import sun.misc.SharedSecrets;
/**
* 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
--- 25,36 ----
package java.lang.ref;
import sun.misc.Cleaner;
import sun.misc.JavaLangRefAccess;
import sun.misc.SharedSecrets;
+ import sun.misc.VM;
/**
* 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
*** 97,246 ****
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
! @SuppressWarnings("rawtypes")
! Reference next;
/* 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
*/
! transient private 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.
*/
static private 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 ManagedLocalsThread {
!
! private static void ensureClassInitialized(Class<?> clazz) {
! try {
! Class.forName(clazz.getName(), true, clazz.getClassLoader());
! } catch (ClassNotFoundException e) {
! 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, name);
! }
!
! 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;
- tgn != null;
- tg = tgn, tgn = tg.getParent());
- Thread handler = new ReferenceHandler(tg, "Reference Handler");
- /* If there were a special system-only priority greater than
- * MAX_PRIORITY, it would be used here
- */
- handler.setPriority(Thread.MAX_PRIORITY);
- handler.setDaemon(true);
- handler.start();
-
- // provide access in SharedSecrets
- SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
- @Override
- public boolean tryHandlePendingReference() {
- return tryHandlePending(false);
}
- });
}
/* -- Referent accessor and setters -- */
/**
--- 97,245 ----
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
! Reference<?> next;
/* 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
*/
! transient private 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.
*/
static private 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<Object> pending = null;
! /* Max. number of references to unhook from pending chain in one chunk
! * before releasing the lock, handling them and grabbing the
! * lock again.
*/
! private static final int CHUNK_SIZE = 256;
/**
! * Try handle a chunk of pending {@link Reference}s if there are any.<p>
! * Return {@code true} as a hint that there are more
! * {@link Reference}s 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 is be more {@link Reference}s pending.
*/
static boolean tryHandlePending(boolean waitForNotify) {
! Reference<?> r = pollPendingChunk(waitForNotify, null);
! if (r == null) return false;
! handlePendingChunk(r);
! synchronized (lock) {
! return pending != null;
! }
! }
!
! /**
! * Polls a chunk of max. {@link #CHUNK_SIZE} references from pending chain and
! * returns the head of the chunk; others can be reached using {@link #next} pointer; the
! * last in chunk is linked to itself.
! *
! * @param waitForNotify if {@code true} and there were no pending
! * {@link Reference}s, wait until notified from VM
! * or interrupted; if {@code false}, return immediately
! * when there are no pending {@link Reference}s.
! * @param morePending if non null, it should be a boolean array with length 1
! * to hold the additional result - a flag indicating that
! * there are more pending references waiting after a chunk
! * of them has been returned.
! * @return the head of the chunk of max. {@link #CHUNK_SIZE} pending references or
! * null if there are none pending.
! */
! static Reference<?> pollPendingChunk(boolean waitForNotify, boolean[] morePending) {
! Reference<?> r = null;
try {
synchronized (lock) {
! if ((r = pending) != null) {
! // pending state invariant established by VM:
! // assert r.next == r;
! // move a chunk of pending/discovered references to a
! // temporary local r/next chain
! Reference<?> rd = r.discovered;
! for (int i = 0; rd != null; rd = r.discovered) {
r.discovered = null;
+ if (++i >= CHUNK_SIZE) {
+ break;
+ }
+ rd.next = r;
+ r = rd;
+ }
+ pending = (Reference) rd;
+ if (morePending != null) morePending[0] = (rd != null);
} else {
+ if (morePending != null) morePending[0] = false;
// The waiting on the lock may cause an OutOfMemoryError
// because it may try to allocate exception objects.
if (waitForNotify) {
lock.wait();
}
}
}
} catch (OutOfMemoryError x) {
// Give other threads CPU time so they hopefully drop some live references
// and GC reclaims some space.
Thread.yield();
} catch (InterruptedException x) {
! // ignore
! }
! return r;
}
+ /**
+ * Handles a non-null chunk of pending references
+ * (obtained using {@link #pollPendingChunk(boolean, boolean[])}) and handles
+ * them as following:
+ * <ul>
+ * <li>Cleaner(s) are executed immediately</li>
+ * <li>Finalizer(s) are submitted as ForkJoinTask(s)</li>
+ * <li>all other Reference(s) are enqueued in their respected queues</li>
+ * </ul>
+ * @param r the head of a chunk of pending references
+ */
+ static void handlePendingChunk(Reference<?> r) {
+ // dispatch temporary local r/next chain to appropriate queues
+ for (Reference<?> rn = r.next; ; r = rn, rn = r.next) {
+ // make 'r' appear to be just taken off the pending chain
+ r.next = r;
// Fast path for cleaners
! if (r instanceof Cleaner) {
! ((Cleaner) r).clean();
! } else if (r instanceof Finalizer) {
! // submit task for finalizers
! new ReferenceHandling.FinalizerHandler((Finalizer) r).submit();
! } else {
! // Enqueue all other references
! ReferenceQueue<?> q = r.queue;
! if (q != ReferenceQueue.NULL) q.enqueue((Reference)r);
}
! if (rn == r) { // last in chain
! break;
}
}
}
/* -- Referent accessor and setters -- */
/**
*** 307,312 ****
--- 306,388 ----
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
+ private static void ensureClassInitialized(Class<?> clazz) {
+ try {
+ Class.forName(clazz.getName(), true, clazz.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
+ }
+ }
+
+ // Unsafe machinery
+
+ @SuppressWarnings("unchecked")
+ T getReferentVolatile() {
+ return (T) UNSAFE.getObjectVolatile(this, referentOffset);
+ }
+
+ boolean casReferent(T cmp, T val) {
+ return UNSAFE.compareAndSwapObject(this, referentOffset, cmp, val);
+ }
+
+ void lazySetQueue(ReferenceQueue<? super T> val) {
+ UNSAFE.putOrderedObject(this, queueOffset, val);
+ }
+
+ boolean casQueue(ReferenceQueue<?> cmp, ReferenceQueue<? super T> val) {
+ return UNSAFE.compareAndSwapObject(this, queueOffset, cmp, val);
+ }
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long referentOffset;
+ private static final long queueOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<Reference> rc = Reference.class;
+ referentOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("referent"));
+ queueOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("queue"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+
+ // pre-load and initialize InterruptedException and Cleaner classes
+ // so that we don't get into trouble later if there's
+ // memory shortage while loading/initializing them lazily.
+ ensureClassInitialized(InterruptedException.class);
+ ensureClassInitialized(Cleaner.class);
+
+ ThreadGroup tg = Thread.currentThread().getThreadGroup();
+ for (ThreadGroup tgn = tg;
+ tgn != null;
+ tg = tgn, tgn = tg.getParent());
+
+ // must wait for VM to boot-up before starting ReferenceHandling
+ // as ForkJoinPool initialization accesses system properties
+ new Thread(tg, "ReferenceHandlingStarter") {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ VM.awaitBooted();
+ break;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ ReferenceHandling.start();
+ }
+ }.start();
+
+ // provide access in SharedSecrets
+ SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
+ @Override
+ public boolean tryHandlePendingReference() {
+ return tryHandlePending(false);
+ }
+ });
+ }
}
< prev index next >