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