< 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 >