1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.ref;
  27 
  28 import sun.misc.JavaLangRefAccess;
  29 import sun.misc.ManagedLocalsThread;
  30 import sun.misc.SharedSecrets;
  31 
  32 /**
  33  * Abstract base class for reference objects.  This class defines the
  34  * operations common to all reference objects.  Because reference objects are
  35  * implemented in close cooperation with the garbage collector, this class may
  36  * not be subclassed directly.
  37  *
  38  * @author   Mark Reinhold
  39  * @since    1.2
  40  */
  41 
  42 public abstract class Reference<T> {
  43 
  44     /* A Reference instance is in one of four possible internal states:
  45      *
  46      *     Active: Subject to special treatment by the garbage collector.  Some
  47      *     time after the collector detects that the reachability of the
  48      *     referent has changed to the appropriate state, it changes the
  49      *     instance's state to either Pending or Inactive, depending upon
  50      *     whether or not the instance was registered with a queue when it was
  51      *     created.  In the former case it also adds the instance to the
  52      *     pending-Reference list.  Newly-created instances are Active.
  53      *
  54      *     Pending: An element of the pending-Reference list, waiting to be
  55      *     enqueued by the Reference-handler thread.  Unregistered instances
  56      *     are never in this state.
  57      *
  58      *     Enqueued: An element of the queue with which the instance was
  59      *     registered when it was created.  When an instance is removed from
  60      *     its ReferenceQueue, it is made Inactive.  Unregistered instances are
  61      *     never in this state.
  62      *
  63      *     Inactive: Nothing more to do.  Once an instance becomes Inactive its
  64      *     state will never change again.
  65      *
  66      * The state is encoded in the queue and next fields as follows:
  67      *
  68      *     Active: queue = ReferenceQueue with which instance is registered, or
  69      *     ReferenceQueue.NULL if it was not registered with a queue; next =
  70      *     null.
  71      *
  72      *     Pending: queue = ReferenceQueue with which instance is registered;
  73      *     next = this
  74      *
  75      *     Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
  76      *     in queue, or this if at end of list.
  77      *
  78      *     Inactive: queue = ReferenceQueue.NULL; next = this.
  79      *
  80      * With this scheme the collector need only examine the next field in order
  81      * to determine whether a Reference instance requires special treatment: If
  82      * the next field is null then the instance is active; if it is non-null,
  83      * then the collector should treat the instance normally.
  84      *
  85      * To ensure that a concurrent collector can discover active Reference
  86      * objects without interfering with application threads that may apply
  87      * the enqueue() method to those objects, collectors should link
  88      * discovered objects through the discovered field. The discovered
  89      * field is also used for linking Reference objects in the pending list.
  90      */
  91 
  92     private T referent;         /* Treated specially by GC */
  93 
  94     volatile ReferenceQueue<? super T> queue;
  95 
  96     /* When active:   NULL
  97      *     pending:   this
  98      *    Enqueued:   next reference in queue (or this if last)
  99      *    Inactive:   this
 100      */
 101     Reference<?> next;
 102 
 103     /* When active:   next element in a discovered reference list maintained by GC (or this if last)
 104      *     pending:   next element in the pending list (or null if last)
 105      *   otherwise:   NULL
 106      */
 107     transient private Reference<?> discovered;  /* used by VM */
 108 
 109 
 110     /* Object used to synchronize with the garbage collector.  The collector
 111      * must acquire this lock at the beginning of each collection cycle.  It is
 112      * therefore critical that any code holding this lock complete as quickly
 113      * as possible, allocate no new objects, and avoid calling user code.
 114      */
 115     static private class Lock { }
 116     private static final Lock lock = new Lock();
 117 
 118 
 119     /* List of References waiting to be enqueued.  The collector adds
 120      * References to this list, while the Reference-handler thread removes
 121      * them.  This list is protected by the above lock object. The
 122      * list uses the discovered field to link its elements.
 123      */
 124     private static Reference<?> pending;
 125 
 126     /**
 127      * Max. number of Reference(s) to unhook from pending chain in one chunk
 128      * before releasing the lock, handling them and grabbing the
 129      * lock again.
 130      */
 131     private static final int UNHOOK_CHUNK_SIZE = 32768;
 132 
 133     /**
 134      * Max. number of j.l.r.Cleaner(s) to execute in one ForkJoinTask
 135      */
 136     private static final int CLEANER_CHUNK_SIZE = 256;
 137 
 138     /**
 139      * Max. number of Reference(s) to enqueue in one chunk
 140      */
 141     private static final int ENQUEUE_CHUNK_SIZE = 256;
 142 
 143     private static class ReferenceHandler extends ManagedLocalsThread {
 144 
 145         private static void ensureClassInitialized(Class<?> clazz) {
 146             try {
 147                 Class.forName(clazz.getName(), true, clazz.getClassLoader());
 148             } catch (ClassNotFoundException e) {
 149                 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
 150             }
 151         }
 152 
 153         static {
 154             // pre-load and initialize InterruptedException and Cleaner classes
 155             // so that we don't get into trouble later in the run loop if there's
 156             // memory shortage while loading/initializing them lazily.
 157             ensureClassInitialized(InterruptedException.class);
 158             ensureClassInitialized(sun.misc.Cleaner.class);
 159             ensureClassInitialized(Cleaner.class);
 160         }
 161 
 162         ReferenceHandler(ThreadGroup g, String name) {
 163             super(g, name);
 164         }
 165 
 166         public void run() {
 167             // wait for VM to boot-up before starting reference handling
 168             // ForkJoinPool since it needs access to some system properties
 169             // and Finalizer needs access to SharedSecrets.
 170             while (true) {
 171                 try {
 172                     sun.misc.VM.awaitBooted();
 173                     break;
 174                 } catch (InterruptedException e) {
 175                     // ignore;
 176                 }
 177             }
 178             // enter endless loop
 179             boolean[] morePending = new boolean[1];
 180             while (true) {
 181                 Reference<?> chunk = null;
 182                 try {
 183                     synchronized (lock) {
 184                         chunk = Reference.unhookPendingChunk(UNHOOK_CHUNK_SIZE, morePending);
 185                         if (chunk == null) {
 186                             // waiting on notification can throw InterruptedException
 187                             // if the thread is interrupted, but also OutOfMemoryError
 188                             // if the InterruptedException can not be allocated.
 189                             lock.wait();
 190                             // since we have already re-obtained the lock, we can
 191                             // re-try poll and will typically get a non-null chunk.
 192                             chunk = Reference.unhookPendingChunk(UNHOOK_CHUNK_SIZE, morePending);
 193                         }
 194                     }
 195                 } catch (OutOfMemoryError e) {
 196                     // give other threads some time so they hopefully release some
 197                     // references and GC reclaims some space, then retry...
 198                     Thread.yield();
 199                 } catch (InterruptedException e) {
 200                     // ignore
 201                 }
 202                 if (chunk != null) {
 203                     if (morePending[0]) {
 204                         // submit a handling task and return for next chunk
 205                         new ReferenceHandling.PendingChunkHandler(chunk).submit();
 206                     } else {
 207                         // no more pending, so we can handle the chunk directly
 208                         Reference.handlePendingChunk(chunk);
 209                     }
 210                 }
 211             }
 212         }
 213     }
 214 
 215     /**
 216      * Try handle a chunk of pending {@link Reference}s if there are any.<p>
 217      * Return {@code true} as a hint that there are more
 218      * {@link Reference}s pending or {@code false} when there are no more pending
 219      * {@link Reference}s at the moment and the program can do some other
 220      * useful work instead of looping.
 221      *
 222      * @return {@code true} if there is be more {@link Reference}s pending.
 223      */
 224     static boolean tryHandlePending() {
 225         Reference<?> r;
 226         synchronized (lock) {
 227             r = unhookPendingChunk(UNHOOK_CHUNK_SIZE, null);
 228         }
 229         if (r == null) return false;
 230         handlePendingChunk(r);
 231         synchronized (lock) {
 232             return pending != null;
 233         }
 234     }
 235 
 236     /**
 237      * Unhooks a chunk of max. {@code chunkSize} references from pending chain and
 238      * returns the head of the chunk; elements of the chunk can be reached using
 239      * {@link #discovered} links; the last in chunk is linked to null.
 240      *
 241      * @param chunkSize max. number of references to unhook from the pending chain
 242      * @param morePending   if non null, it should be a boolean array with length 1
 243      *                      to hold the additional result - a flag indicating that
 244      *                      there are more pending references waiting after a chunk
 245      *                      of them has been returned.
 246      * @return the head of the chunk of max. {@code chunkSize} pending references or
 247      * null if there are none pending.
 248      */
 249     private static Reference<?> unhookPendingChunk(int chunkSize, boolean[] morePending) {
 250         // assert Thread.holdsLock(lock);
 251         Reference<?> r;
 252         if ((r = pending) != null) {
 253             // skip over max. chunkSize references using 'discovered' link
 254             Reference<?> p = r;
 255             Reference<?> d = p.discovered;
 256             for (int i = 0;
 257                  d != null && ++i < chunkSize;
 258                  p = d, d = p.discovered) {}
 259             pending = d;
 260             p.discovered = null; // unlink last in unhooked chunk from the rest
 261             if (morePending != null) morePending[0] = (d != null);
 262         } else {
 263             if (morePending != null) morePending[0] = false;
 264         }
 265         return r;
 266     }
 267 
 268     /**
 269      * Takes a non-null chunk of unhooked pending references
 270      * (obtained using {@link #unhookPendingChunk}) and handles
 271      * them as following:
 272      * <ul>
 273      * <li>sun.misc.Cleaner(s) are executed immediately</li>
 274      * <li>java.lang.ref.Cleaner(s) are submitted in chunks as ForkJoinTask(s)</li>
 275      * <li>all other Reference(s) are enqueued in their respective queues</li>
 276      * </ul>
 277      * The references in chunk are linked using {@link #discovered} link.
 278      * Last in chunk is linked to null. As they are handled, their discovered
 279      * links are reset to null.
 280      *
 281      * @param chunk the head of a chunk of pending references
 282      */
 283     static void handlePendingChunk(Reference<?> chunk) {
 284         // batch j.l.r.Cleaner(s)
 285         Reference<?> cleaners = null;
 286         int cleanersCount = 0;
 287         // batch runs of consecutive references having same queue
 288         Reference<?> referencesHead = null, referencesTail = null;
 289         int referencesCount = 0;
 290         ReferenceQueue<?> referenceQueue = null;
 291         // dispatch references to appropriate targets
 292         for (Reference<?> r = chunk, d = r.discovered;
 293              r != null;
 294              r = d, d = (r == null) ? null : r.discovered) {
 295             // invariant established by GC when marking the reference as not active
 296             // assert r.next == r;
 297             if (r instanceof sun.misc.Cleaner) {  // Fast path for sun.misc.Cleaners
 298                 // unlink from the rest in chunk
 299                 r.discovered = null;
 300                 ((sun.misc.Cleaner) r).clean();
 301             } else if (r instanceof Cleaner) {    // Submit task(s) for j.l.r.Cleaner(s)
 302                 // link into the local cleaners list
 303                 r.discovered = cleaners;
 304                 cleaners = r;
 305                 if (++cleanersCount >= CLEANER_CHUNK_SIZE) {
 306                     // when chunk of finalizers is full, submit a task
 307                     new ReferenceHandling.CleanersHandler(cleaners).submit();
 308                     cleaners = null;
 309                     cleanersCount = 0;
 310                 }
 311             } else {                              // Enqueue all other references
 312                 // unlink from the rest in chunk
 313                 r.discovered = null;
 314                 ReferenceQueue<?> q = r.queue;
 315                 if (q != ReferenceQueue.NULL && q.markEnqueued(r)) { // markEnqueued is atomic
 316                     if (referenceQueue == null || referenceQueue == q) {
 317                         // no queue or same queue -> hook onto the references[Head|Tail] chain
 318                         if (referencesHead == null) {
 319                             // assert referencesTail == null && referenceQueue == null &&
 320                             //        referencesCount == 0 && r.next == r;
 321                             referenceQueue = q;
 322                             referencesHead = referencesTail = r;
 323                         } else {
 324                             // assert referencesTail != null && referenceQueue == q &&
 325                             //        referencesCount > 0;
 326                             r.next = referencesHead;
 327                             referencesHead = r;
 328                         }
 329                         if (++referencesCount >= ENQUEUE_CHUNK_SIZE) {
 330                             // when a chunk of references is full, add them to queue
 331                             referenceQueue.addChunk(referencesHead, referencesTail);
 332                             referencesHead = referencesTail = null;
 333                             referenceQueue = null;
 334                             referencesCount = 0;
 335                         }
 336                     } else {
 337                         // when a different queue is encountered,
 338                         // add collected chunk to it's queue and start collecting
 339                         // new batch for new queue...
 340                         // assert referenceQueue != null && referenceQueue != q &&
 341                         //        referencesHead != null && referencesTail != null &&
 342                         //        referencesCount > 0 && r.next == r;
 343                         referenceQueue.addChunk(referencesHead, referencesTail);
 344                         referenceQueue = q;
 345                         referencesHead = referencesTail = r;
 346                         referencesCount = 1;
 347                     }
 348                 }
 349                 // else just drop it on the flor
 350             }
 351         }
 352         // any j.l.r.Cleaner(s) left?
 353         if (cleaners != null) {
 354             new ReferenceHandling.CleanersHandler(cleaners).submit();
 355             cleaners = null;
 356             cleanersCount = 0;
 357         }
 358         // any references left to enqueue?
 359         if (referenceQueue != null) {
 360             // assert referencesHead != null && referencesTail != null && referencesCount > 0;
 361             referenceQueue.addChunk(referencesHead, referencesTail);
 362             referencesHead = referencesTail = null;
 363             referenceQueue = null;
 364             referencesCount = 0;
 365         }
 366     }
 367 
 368     /**
 369      * Handles a non-null chunk of j.l.r.Cleaner(s)
 370      *
 371      * @param cleaners the head of a chunk of Reference(s) implementing j.l.r.Cleaner
 372      *                 linked using {@link #discovered} link. Last in chunk is
 373      *                 linked to null.
 374      */
 375     static void handleCleaners(Reference<?> cleaners) {
 376         for (Reference<?> c = cleaners, d = c.discovered;
 377              c != null;
 378              c = d, d = (c == null) ? null : c.discovered) {
 379             // unlink it from the rest in chunk
 380             c.discovered = null;
 381             // Handle it
 382             try {
 383                 ((Cleaner) c).clean();
 384             } catch (Throwable t) {
 385                 // ignore
 386             }
 387         }
 388     }
 389 
 390     /* -- Referent accessor and setters -- */
 391 
 392     /**
 393      * Returns this reference object's referent.  If this reference object has
 394      * been cleared, either by the program or by the garbage collector, then
 395      * this method returns <code>null</code>.
 396      *
 397      * @return   The object to which this reference refers, or
 398      *           <code>null</code> if this reference object has been cleared
 399      */
 400     public T get() {
 401         return this.referent;
 402     }
 403 
 404     /**
 405      * Clears this reference object.  Invoking this method will not cause this
 406      * object to be enqueued.
 407      *
 408      * <p> This method is invoked only by Java code; when the garbage collector
 409      * clears references it does so directly, without invoking this method.
 410      */
 411     public void clear() {
 412         this.referent = null;
 413     }
 414 
 415 
 416     /* -- Queue operations -- */
 417 
 418     /**
 419      * Tells whether or not this reference object has been enqueued, either by
 420      * the program or by the garbage collector.  If this reference object was
 421      * not registered with a queue when it was created, then this method will
 422      * always return <code>false</code>.
 423      *
 424      * @return   <code>true</code> if and only if this reference object has
 425      *           been enqueued
 426      */
 427     public boolean isEnqueued() {
 428         return (this.queue == ReferenceQueue.ENQUEUED);
 429     }
 430 
 431     /**
 432      * Adds this reference object to the queue with which it is registered,
 433      * if any.
 434      *
 435      * <p> This method is invoked only by Java code; when the garbage collector
 436      * enqueues references it does so directly, without invoking this method.
 437      *
 438      * @return   <code>true</code> if this reference object was successfully
 439      *           enqueued; <code>false</code> if it was already enqueued or if
 440      *           it was not registered with a queue when it was created
 441      */
 442     public boolean enqueue() {
 443         return this.queue.enqueue(this);
 444     }
 445 
 446 
 447     /* -- Constructors -- */
 448 
 449     Reference(T referent) {
 450         this(referent, null);
 451     }
 452 
 453     Reference(T referent, ReferenceQueue<? super T> queue) {
 454         this.referent = referent;
 455         if (this instanceof Cleaner && queue != null) {
 456             throw new IllegalArgumentException(
 457                 "Reference implementing Cleaner can't be registered with a reference queue");
 458         }
 459         this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
 460     }
 461 
 462     // Methods that enable selected Reference subclasses to be elements of a DLList
 463 
 464     /**
 465      * Some Cleaner's (such as Finalizer) wish to be registered in a doubly-linked
 466      * list so that they are kept alive until discovered by VM, processed by
 467      * reference handling threads and then unlinked. There are several lists to
 468      * distribute them randomly into to reduce contention among concurrent threads
 469      * trying to link/unlink them.
 470      */
 471     static final DLList[] uncleanedLists;
 472 
 473     /**
 474      * Register this reference into the associated doubly-linked list.
 475      */
 476     final void link() {
 477         int h = System.identityHashCode(this);
 478         int i = (h >>> 1) & (uncleanedLists.length - 1);
 479         uncleanedLists[i].link(this, (h & 1) == 0);
 480     }
 481 
 482     /**
 483      * De-register this reference from the associated doubly-linked list.
 484      */
 485     final void unlink() {
 486         int h = System.identityHashCode(this);
 487         int i = (h >>> 1) & (uncleanedLists.length - 1);
 488         uncleanedLists[i].unlink(this);
 489     }
 490 
 491     boolean isDeletedDll() {
 492         throw new UnsupportedOperationException();
 493     }
 494 
 495     Reference<?> getPrevDll() {
 496         throw new UnsupportedOperationException();
 497     }
 498 
 499     void lazySetPrevDll(Reference<?> val) {
 500         throw new UnsupportedOperationException();
 501     }
 502 
 503     boolean casPrevDll(Reference<?> cmp, Reference<?> val) {
 504         throw new UnsupportedOperationException();
 505     }
 506 
 507     Reference<?> getNextDll() {
 508         throw new UnsupportedOperationException();
 509     }
 510 
 511     void lazySetNextDll(Reference<?> val) {
 512         throw new UnsupportedOperationException();
 513     }
 514 
 515     boolean casNextDll(Reference<?> cmp, Reference<?> val) {
 516         throw new UnsupportedOperationException();
 517     }
 518 
 519     // Unsafe machinery
 520 
 521     @SuppressWarnings("unchecked")
 522     T getReferentVolatile() {
 523         return (T) UNSAFE.getObjectVolatile(this, referentOffset);
 524     }
 525 
 526     boolean casReferent(T cmp, T val) {
 527         return UNSAFE.compareAndSwapObject(this, referentOffset, cmp, val);
 528     }
 529 
 530     void lazySetQueue(ReferenceQueue<? super T> val) {
 531         UNSAFE.putOrderedObject(this, queueOffset, val);
 532     }
 533 
 534     boolean casQueue(ReferenceQueue<?> cmp, ReferenceQueue<? super T> val) {
 535         return UNSAFE.compareAndSwapObject(this, queueOffset, cmp, val);
 536     }
 537 
 538     private static final sun.misc.Unsafe UNSAFE;
 539     private static final long referentOffset;
 540     private static final long queueOffset;
 541 
 542     static {
 543         try {
 544             UNSAFE = sun.misc.Unsafe.getUnsafe();
 545             Class<Reference> rc = Reference.class;
 546             referentOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("referent"));
 547             queueOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("queue"));
 548         } catch (Exception e) {
 549             throw new Error(e);
 550         }
 551 
 552         // DLList(s) for selected uncleaned Cleaner(s)
 553         int cpus = Runtime.getRuntime().availableProcessors();
 554         // smallest power of two equal or greater than 2 * # of CPUs
 555         int lists = (cpus <= 1) ? 2 : Integer.highestOneBit(cpus - 1) << 2;
 556         uncleanedLists = new DLList[lists];
 557         for (int i = 0; i < uncleanedLists.length; i++) {
 558             uncleanedLists[i] = new DLList();
 559         }
 560 
 561         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 562         for (ThreadGroup tgn = tg;
 563              tgn != null;
 564              tg = tgn, tgn = tg.getParent());
 565         Thread handler = new ReferenceHandler(tg, "Reference Handler");
 566         /* If there were a special system-only priority greater than
 567          * MAX_PRIORITY, it would be used here
 568          */
 569         handler.setPriority(Thread.MAX_PRIORITY);
 570         handler.setDaemon(true);
 571         handler.start();
 572 
 573         // provide access in SharedSecrets
 574         SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
 575             @Override
 576             public boolean tryHandlePendingReference() {
 577                 return tryHandlePending();
 578             }
 579         });
 580     }
 581 }