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 }