82 * to determine whether a Reference instance requires special treatment: If 83 * the next field is null then the instance is active; if it is non-null, 84 * then the collector should treat the instance normally. 85 * 86 * To ensure that a concurrent collector can discover active Reference 87 * objects without interfering with application threads that may apply 88 * the enqueue() method to those objects, collectors should link 89 * discovered objects through the discovered field. The discovered 90 * field is also used for linking Reference objects in the pending list. 91 */ 92 93 private T referent; /* Treated specially by GC */ 94 95 volatile ReferenceQueue<? super T> queue; 96 97 /* When active: NULL 98 * pending: this 99 * Enqueued: next reference in queue (or this if last) 100 * Inactive: this 101 */ 102 @SuppressWarnings("rawtypes") 103 Reference next; 104 105 /* When active: next element in a discovered reference list maintained by GC (or this if last) 106 * pending: next element in the pending list (or null if last) 107 * otherwise: NULL 108 */ 109 transient private Reference<T> discovered; /* used by VM */ 110 111 112 /* Object used to synchronize with the garbage collector. The collector 113 * must acquire this lock at the beginning of each collection cycle. It is 114 * therefore critical that any code holding this lock complete as quickly 115 * as possible, allocate no new objects, and avoid calling user code. 116 */ 117 static private class Lock { } 118 private static Lock lock = new Lock(); 119 120 121 /* List of References waiting to be enqueued. The collector adds 122 * References to this list, while the Reference-handler thread removes 123 * them. This list is protected by the above lock object. The 124 * list uses the discovered field to link its elements. 125 */ 126 private static Reference<Object> pending = null; 127 128 /* High-priority thread to enqueue pending References 129 */ 130 private static class ReferenceHandler extends ManagedLocalsThread { 131 132 private static void ensureClassInitialized(Class<?> clazz) { 133 try { 134 Class.forName(clazz.getName(), true, clazz.getClassLoader()); 135 } catch (ClassNotFoundException e) { 136 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); 137 } 138 } 139 140 static { 141 // pre-load and initialize InterruptedException and Cleaner classes 142 // so that we don't get into trouble later in the run loop if there's 143 // memory shortage while loading/initializing them lazily. 144 ensureClassInitialized(InterruptedException.class); 145 ensureClassInitialized(Cleaner.class); 146 } 147 148 ReferenceHandler(ThreadGroup g, String name) { 149 super(g, name); 150 } 151 152 public void run() { 153 while (true) { 154 tryHandlePending(true); 155 } 156 } 157 } 158 159 /** 160 * Try handle pending {@link Reference} if there is one.<p> 161 * Return {@code true} as a hint that there might be another 162 * {@link Reference} pending or {@code false} when there are no more pending 163 * {@link Reference}s at the moment and the program can do some other 164 * useful work instead of looping. 165 * 166 * @param waitForNotify if {@code true} and there was no pending 167 * {@link Reference}, wait until notified from VM 168 * or interrupted; if {@code false}, return immediately 169 * when there is no pending {@link Reference}. 170 * @return {@code true} if there was a {@link Reference} pending and it 171 * was processed, or we waited for notification and either got it 172 * or thread was interrupted before being notified; 173 * {@code false} otherwise. 174 */ 175 static boolean tryHandlePending(boolean waitForNotify) { 176 Reference<Object> r; 177 Cleaner c; 178 try { 179 synchronized (lock) { 180 if (pending != null) { 181 r = pending; 182 // 'instanceof' might throw OutOfMemoryError sometimes 183 // so do this before un-linking 'r' from the 'pending' chain... 184 c = r instanceof Cleaner ? (Cleaner) r : null; 185 // unlink 'r' from 'pending' chain 186 pending = r.discovered; 187 r.discovered = null; 188 } else { 189 // The waiting on the lock may cause an OutOfMemoryError 190 // because it may try to allocate exception objects. 191 if (waitForNotify) { 192 lock.wait(); 193 } 194 // retry if waited 195 return waitForNotify; 196 } 197 } 198 } catch (OutOfMemoryError x) { 199 // Give other threads CPU time so they hopefully drop some live references 200 // and GC reclaims some space. 201 // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above 202 // persistently throws OOME for some time... 203 Thread.yield(); 204 // retry 205 return true; 206 } catch (InterruptedException x) { 207 // retry 208 return true; 209 } 210 211 // Fast path for cleaners 212 if (c != null) { 213 c.clean(); 214 return true; 215 } 216 217 ReferenceQueue<? super Object> q = r.queue; 218 if (q != ReferenceQueue.NULL) q.enqueue(r); 219 return true; 220 } 221 222 static { 223 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 224 for (ThreadGroup tgn = tg; 225 tgn != null; 226 tg = tgn, tgn = tg.getParent()); 227 Thread handler = new ReferenceHandler(tg, "Reference Handler"); 228 /* If there were a special system-only priority greater than 229 * MAX_PRIORITY, it would be used here 230 */ 231 handler.setPriority(Thread.MAX_PRIORITY); 232 handler.setDaemon(true); 233 handler.start(); 234 235 // provide access in SharedSecrets 236 SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { 237 @Override 238 public boolean tryHandlePendingReference() { 239 return tryHandlePending(false); 240 } 241 }); 242 } 243 244 /* -- Referent accessor and setters -- */ 245 246 /** 247 * Returns this reference object's referent. If this reference object has 248 * been cleared, either by the program or by the garbage collector, then 249 * this method returns <code>null</code>. 250 * 251 * @return The object to which this reference refers, or 252 * <code>null</code> if this reference object has been cleared 253 */ 254 public T get() { 255 return this.referent; 256 } 257 258 /** 259 * Clears this reference object. Invoking this method will not cause this 260 * object to be enqueued. 261 * 292 * @return <code>true</code> if this reference object was successfully 293 * enqueued; <code>false</code> if it was already enqueued or if 294 * it was not registered with a queue when it was created 295 */ 296 public boolean enqueue() { 297 return this.queue.enqueue(this); 298 } 299 300 301 /* -- Constructors -- */ 302 303 Reference(T referent) { 304 this(referent, null); 305 } 306 307 Reference(T referent, ReferenceQueue<? super T> queue) { 308 this.referent = referent; 309 this.queue = (queue == null) ? ReferenceQueue.NULL : queue; 310 } 311 312 } | 82 * to determine whether a Reference instance requires special treatment: If 83 * the next field is null then the instance is active; if it is non-null, 84 * then the collector should treat the instance normally. 85 * 86 * To ensure that a concurrent collector can discover active Reference 87 * objects without interfering with application threads that may apply 88 * the enqueue() method to those objects, collectors should link 89 * discovered objects through the discovered field. The discovered 90 * field is also used for linking Reference objects in the pending list. 91 */ 92 93 private T referent; /* Treated specially by GC */ 94 95 volatile ReferenceQueue<? super T> queue; 96 97 /* When active: NULL 98 * pending: this 99 * Enqueued: next reference in queue (or this if last) 100 * Inactive: this 101 */ 102 Reference<?> next; 103 104 /* When active: next element in a discovered reference list maintained by GC (or this if last) 105 * pending: next element in the pending list (or null if last) 106 * otherwise: NULL 107 */ 108 transient private Reference<?> discovered; /* used by VM */ 109 110 111 /* Object used to synchronize with the garbage collector. The collector 112 * must acquire this lock at the beginning of each collection cycle. It is 113 * therefore critical that any code holding this lock complete as quickly 114 * as possible, allocate no new objects, and avoid calling user code. 115 */ 116 static private class Lock { } 117 private static final Lock lock = new Lock(); 118 119 120 /* List of References waiting to be enqueued. The collector adds 121 * References to this list, while the Reference-handler thread removes 122 * them. This list is protected by the above lock object. The 123 * list uses the discovered field to link its elements. 124 */ 125 private static Reference<Object> pending = null; 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 /* Max. number of Finalizer(s) to execute in one ForkJoinTask 134 */ 135 private static final int FINALIZE_CHUNK_SIZE = 256; 136 137 /* Max. number of Reference(s) to enqueue in one chunk 138 */ 139 private static final int ENQUEUE_CHUNK_SIZE = 256; 140 141 private static class ReferenceHandler extends ManagedLocalsThread { 142 143 private static void ensureClassInitialized(Class<?> clazz) { 144 try { 145 Class.forName(clazz.getName(), true, clazz.getClassLoader()); 146 } catch (ClassNotFoundException e) { 147 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); 148 } 149 } 150 151 static { 152 // pre-load and initialize InterruptedException and Cleaner classes 153 // so that we don't get into trouble later in the run loop if there's 154 // memory shortage while loading/initializing them lazily. 155 ensureClassInitialized(InterruptedException.class); 156 ensureClassInitialized(Cleaner.class); 157 } 158 159 ReferenceHandler(ThreadGroup g, String name) { 160 super(g, name); 161 } 162 163 public void run() { 164 // wait for VM to boot-up before starting reference handling 165 // ForkJoinPool since it needs access to some system properties 166 // and Finalizer needs access to SharedSecrets. 167 while (true) { 168 try { 169 sun.misc.VM.awaitBooted(); 170 break; 171 } catch (InterruptedException e) { 172 // ignore; 173 } 174 } 175 // start reference handling ForkJoinPool 176 ReferenceHandling.start(); 177 // enter endless loop 178 boolean[] morePending = new boolean[1]; 179 while (true) { 180 Reference<?> chunk = null; 181 try { 182 synchronized (lock) { 183 chunk = Reference.unhookPendingChunk(UNHOOK_CHUNK_SIZE, morePending); 184 if (chunk == null) { 185 // waiting on notification can throw InterruptedException 186 // if the thread is interrupted, but also OutOfMemoryError 187 // if the InterruptedException can not be allocated. 188 lock.wait(); 189 // since we have already re-obtained the lock, we can 190 // re-try poll and will typically get a non-null chunk. 191 chunk = Reference.unhookPendingChunk(UNHOOK_CHUNK_SIZE, morePending); 192 } 193 } 194 } catch (OutOfMemoryError e) { 195 // give other threads some time so they hopefully release some 196 // references and GC reclaims some space, then retry... 197 Thread.yield(); 198 } catch (InterruptedException e) { 199 // ignore 200 } 201 if (chunk != null) { 202 if (morePending[0]) { 203 // submit a handling task and return for next chunk 204 new ReferenceHandling.PendingChunkHandler(chunk).submit(); 205 } else { 206 // no more pending, so we can handle the chunk directly 207 Reference.handlePendingChunk(chunk); 208 } 209 } 210 } 211 } 212 } 213 214 /** 215 * Try handle a chunk of pending {@link Reference}s if there are any.<p> 216 * Return {@code true} as a hint that there are more 217 * {@link Reference}s pending or {@code false} when there are no more pending 218 * {@link Reference}s at the moment and the program can do some other 219 * useful work instead of looping. 220 * 221 * @return {@code true} if there is be more {@link Reference}s pending. 222 */ 223 static boolean tryHandlePending() { 224 Reference<?> r; 225 synchronized (lock) { 226 r = unhookPendingChunk(UNHOOK_CHUNK_SIZE, null); 227 } 228 if (r == null) return false; 229 handlePendingChunk(r); 230 synchronized (lock) { 231 return pending != null; 232 } 233 } 234 235 /** 236 * Unhooks a chunk of max. {@code chunkSize} references from pending chain and 237 * returns the head of the chunk; elements of the chunk can be reached using 238 * {@link #next} links; the last in chunk is linked to itself. 239 * 240 * @param chunkSize max. number of references to unhook from the pending chain 241 * @param morePending if non null, it should be a boolean array with length 1 242 * to hold the additional result - a flag indicating that 243 * there are more pending references waiting after a chunk 244 * of them has been returned. 245 * @return the head of the chunk of max. {@code chunkSize} pending references or 246 * null if there are none pending. 247 */ 248 private static Reference<?> unhookPendingChunk(int chunkSize, boolean[] morePending) { 249 // assert Thread.holdsLock(lock); 250 Reference<?> r; 251 if ((r = pending) != null) { 252 // pending state invariant established by VM: 253 // assert r.next == r; 254 // move a chunk of pending/discovered references to a 255 // temporary local r/next chain 256 Reference<?> rd = r.discovered; 257 for (int i = 0; rd != null; rd = r.discovered) { 258 r.discovered = null; 259 if (++i >= chunkSize) { 260 break; 261 } 262 rd.next = r; 263 r = rd; 264 } 265 pending = (Reference) rd; 266 if (morePending != null) morePending[0] = (rd != null); 267 } else { 268 if (morePending != null) morePending[0] = false; 269 } 270 return r; 271 } 272 273 /** 274 * Handles a non-null chunk of pending references 275 * (obtained using {@link #unhookPendingChunk}) and handles 276 * them as following: 277 * <ul> 278 * <li>Cleaner(s) are executed immediately</li> 279 * <li>Finalizer(s) are submitted as ForkJoinTask(s)</li> 280 * <li>all other Reference(s) are enqueued in their respected queues</li> 281 * </ul> 282 * @param chunk the head of a chunk of pending references 283 */ 284 static void handlePendingChunk(Reference<?> chunk) { 285 // batch finaliz(ato|e)rs and Finalizators 286 Reference<?> finalizrs = null; 287 int finalizrsCount = 0; 288 // batch consecutive references with same queue into chunks 289 Reference<?> referencesHead = null, referencesTail = null; 290 int referencesCount = 0; 291 ReferenceQueue<?> referenceQueue = null; 292 // dispatch references to appropriate targets 293 for (Reference<?> r = chunk, rn = r.next; ; r = rn, rn = r.next) { 294 if (r instanceof Cleaner) { // Fast path for cleaners 295 // take 'r' off the chain 296 r.next = r; 297 ((Cleaner) r).clean(); 298 } else if (r instanceof Finalizer || 299 r instanceof Finalizator) { // Submit task(s) for finaliz(ato|e)rs 300 // hook onto the finalizers chain 301 r.next = (finalizrs == null) ? r : finalizrs; 302 finalizrs = r; 303 if (++finalizrsCount >= FINALIZE_CHUNK_SIZE) { 304 // when chunk of finaliz(ato|e)rs is full, submit a task 305 new ReferenceHandling.FinalizrHandler(finalizrs).submit(); 306 finalizrs = null; 307 finalizrsCount = 0; 308 } 309 } else { // Enqueue all other references 310 // take 'r' off the chain 311 r.next = r; 312 ReferenceQueue<?> q = r.queue; 313 if (q != ReferenceQueue.NULL && q.markEnqueued(r)) { // markEnqueued is atomic 314 if (referenceQueue == null || referenceQueue == q) { 315 // no queue or same queue -> hook onto the references[Head|Tail] chain 316 if (referencesHead == null) { 317 // assert referencesTail == null && referenceQueue == null && 318 // referencesCount == 0 && r.next == r; 319 referenceQueue = q; 320 referencesHead = referencesTail = r; 321 } else { 322 // assert referencesTail != null && referenceQueue == q && 323 // referencesCount > 0; 324 r.next = referencesHead; 325 referencesHead = r; 326 } 327 if (++referencesCount >= ENQUEUE_CHUNK_SIZE) { 328 // when a chunk of references is full, add them to queue 329 referenceQueue.addChunk(referencesHead, referencesTail); 330 referencesHead = referencesTail = null; 331 referenceQueue = null; 332 referencesCount = 0; 333 } 334 } else { 335 // when a different queue is encountered, 336 // add collected chunk to it's queue and start collecting 337 // into new queue... 338 // assert referenceQueue != null && referenceQueue != q && 339 // referencesHead != null && referencesTail != null && 340 // referencesCount > 0 && r.next == r; 341 referenceQueue.addChunk(referencesHead, referencesTail); 342 referenceQueue = q; 343 referencesHead = referencesTail = r; 344 referencesCount = 1; 345 } 346 } 347 } 348 if (rn == r) { // last in chain 349 break; 350 } 351 } 352 // any finalizers left? 353 if (finalizrs != null) { 354 new ReferenceHandling.FinalizrHandler(finalizrs).submit(); 355 finalizrs = null; 356 finalizrsCount = 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 /* -- Referent accessor and setters -- */ 369 370 /** 371 * Returns this reference object's referent. If this reference object has 372 * been cleared, either by the program or by the garbage collector, then 373 * this method returns <code>null</code>. 374 * 375 * @return The object to which this reference refers, or 376 * <code>null</code> if this reference object has been cleared 377 */ 378 public T get() { 379 return this.referent; 380 } 381 382 /** 383 * Clears this reference object. Invoking this method will not cause this 384 * object to be enqueued. 385 * 416 * @return <code>true</code> if this reference object was successfully 417 * enqueued; <code>false</code> if it was already enqueued or if 418 * it was not registered with a queue when it was created 419 */ 420 public boolean enqueue() { 421 return this.queue.enqueue(this); 422 } 423 424 425 /* -- Constructors -- */ 426 427 Reference(T referent) { 428 this(referent, null); 429 } 430 431 Reference(T referent, ReferenceQueue<? super T> queue) { 432 this.referent = referent; 433 this.queue = (queue == null) ? ReferenceQueue.NULL : queue; 434 } 435 436 // Unsafe machinery 437 438 @SuppressWarnings("unchecked") 439 T getReferentVolatile() { 440 return (T) UNSAFE.getObjectVolatile(this, referentOffset); 441 } 442 443 boolean casReferent(T cmp, T val) { 444 return UNSAFE.compareAndSwapObject(this, referentOffset, cmp, val); 445 } 446 447 void lazySetQueue(ReferenceQueue<? super T> val) { 448 UNSAFE.putOrderedObject(this, queueOffset, val); 449 } 450 451 boolean casQueue(ReferenceQueue<?> cmp, ReferenceQueue<? super T> val) { 452 return UNSAFE.compareAndSwapObject(this, queueOffset, cmp, val); 453 } 454 455 private static final sun.misc.Unsafe UNSAFE; 456 private static final long referentOffset; 457 private static final long queueOffset; 458 459 static { 460 try { 461 UNSAFE = sun.misc.Unsafe.getUnsafe(); 462 Class<Reference> rc = Reference.class; 463 referentOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("referent")); 464 queueOffset = UNSAFE.objectFieldOffset(rc.getDeclaredField("queue")); 465 } catch (Exception e) { 466 throw new Error(e); 467 } 468 469 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 470 for (ThreadGroup tgn = tg; 471 tgn != null; 472 tg = tgn, tgn = tg.getParent()); 473 Thread handler = new ReferenceHandler(tg, "Reference Handler"); 474 /* If there were a special system-only priority greater than 475 * MAX_PRIORITY, it would be used here 476 */ 477 handler.setPriority(Thread.MAX_PRIORITY); 478 handler.setDaemon(true); 479 handler.start(); 480 481 // provide access in SharedSecrets 482 SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { 483 @Override 484 public boolean tryHandlePendingReference() { 485 return tryHandlePending(); 486 } 487 }); 488 } 489 } |