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