93 94 private T referent; /* Treated specially by GC */ 95 96 volatile ReferenceQueue<? super T> queue; 97 98 /* When active: NULL 99 * pending: this 100 * Enqueued: next reference in queue (or this if last) 101 * Inactive: this 102 */ 103 @SuppressWarnings("rawtypes") 104 volatile Reference next; 105 106 /* When active: next element in a discovered reference list maintained by GC (or this if last) 107 * pending: next element in the pending list (or null if last) 108 * otherwise: NULL 109 */ 110 private transient Reference<T> discovered; /* used by VM */ 111 112 113 /* Object used to synchronize with the garbage collector. The collector 114 * must acquire this lock at the beginning of each collection cycle. It is 115 * therefore critical that any code holding this lock complete as quickly 116 * as possible, allocate no new objects, and avoid calling user code. 117 */ 118 private static class Lock { } 119 private static Lock lock = new Lock(); 120 121 122 /* List of References waiting to be enqueued. The collector adds 123 * References to this list, while the Reference-handler thread removes 124 * them. This list is protected by the above lock object. The 125 * list uses the discovered field to link its elements. 126 */ 127 private static Reference<Object> pending = null; 128 129 /* High-priority thread to enqueue pending References 130 */ 131 private static class ReferenceHandler extends Thread { 132 133 private static void ensureClassInitialized(Class<?> clazz) { 134 try { 135 Class.forName(clazz.getName(), true, clazz.getClassLoader()); 136 } catch (ClassNotFoundException e) { 137 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); 138 } 139 } 140 141 static { 142 // pre-load and initialize InterruptedException and Cleaner classes 143 // so that we don't get into trouble later in the run loop if there's 144 // memory shortage while loading/initializing them lazily. 145 ensureClassInitialized(InterruptedException.class); 146 ensureClassInitialized(Cleaner.class); 147 } 148 149 ReferenceHandler(ThreadGroup g, String name) { 150 super(g, null, name, 0, false); 151 } 152 153 public void run() { 154 while (true) { 155 tryHandlePending(true); 156 } 157 } 158 } 159 160 /** 161 * Try handle pending {@link Reference} if there is one.<p> 162 * Return {@code true} as a hint that there might be another 163 * {@link Reference} pending or {@code false} when there are no more pending 164 * {@link Reference}s at the moment and the program can do some other 165 * useful work instead of looping. 166 * 167 * @param waitForNotify if {@code true} and there was no pending 168 * {@link Reference}, wait until notified from VM 169 * or interrupted; if {@code false}, return immediately 170 * when there is no pending {@link Reference}. 171 * @return {@code true} if there was a {@link Reference} pending and it 172 * was processed, or we waited for notification and either got it 173 * or thread was interrupted before being notified; 174 * {@code false} otherwise. 175 */ 176 static boolean tryHandlePending(boolean waitForNotify) { 177 Reference<Object> r; 178 Cleaner c; 179 try { 180 synchronized (lock) { 181 if (pending != null) { 182 r = pending; 183 // 'instanceof' might throw OutOfMemoryError sometimes 184 // so do this before un-linking 'r' from the 'pending' chain... 185 c = r instanceof Cleaner ? (Cleaner) r : null; 186 // unlink 'r' from 'pending' chain 187 pending = r.discovered; 188 r.discovered = null; 189 } else { 190 // The waiting on the lock may cause an OutOfMemoryError 191 // because it may try to allocate exception objects. 192 if (waitForNotify) { 193 lock.wait(); 194 } 195 // retry if waited 196 return waitForNotify; 197 } 198 } 199 } catch (OutOfMemoryError x) { 200 // Give other threads CPU time so they hopefully drop some live references 201 // and GC reclaims some space. 202 // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above 203 // persistently throws OOME for some time... 204 Thread.yield(); 205 // retry 206 return true; 207 } catch (InterruptedException x) { 208 // retry 209 return true; 210 } 211 212 // Fast path for cleaners 213 if (c != null) { 214 c.clean(); 215 return true; 216 } 217 218 ReferenceQueue<? super Object> q = r.queue; 219 if (q != ReferenceQueue.NULL) q.enqueue(r); 220 return true; 221 } 222 223 static { 224 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 225 for (ThreadGroup tgn = tg; 226 tgn != null; 227 tg = tgn, tgn = tg.getParent()); 228 Thread handler = new ReferenceHandler(tg, "Reference Handler"); 229 /* If there were a special system-only priority greater than 230 * MAX_PRIORITY, it would be used here 231 */ 232 handler.setPriority(Thread.MAX_PRIORITY); 233 handler.setDaemon(true); 234 handler.start(); 235 236 // provide access in SharedSecrets 237 SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { 238 @Override 239 public boolean tryHandlePendingReference() { | 93 94 private T referent; /* Treated specially by GC */ 95 96 volatile ReferenceQueue<? super T> queue; 97 98 /* When active: NULL 99 * pending: this 100 * Enqueued: next reference in queue (or this if last) 101 * Inactive: this 102 */ 103 @SuppressWarnings("rawtypes") 104 volatile Reference next; 105 106 /* When active: next element in a discovered reference list maintained by GC (or this if last) 107 * pending: next element in the pending list (or null if last) 108 * otherwise: NULL 109 */ 110 private transient Reference<T> discovered; /* used by VM */ 111 112 113 /* Blocks until the reference pending list becomes non-empty and 114 * returns the list. 115 */ 116 private static native Reference<Object> getReferencePendingList(); 117 118 119 /* High-priority thread to enqueue pending References 120 */ 121 private static class ReferenceHandler extends Thread { 122 123 private static void ensureClassInitialized(Class<?> clazz) { 124 try { 125 Class.forName(clazz.getName(), true, clazz.getClassLoader()); 126 } catch (ClassNotFoundException e) { 127 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); 128 } 129 } 130 131 static { 132 // pre-load and initialize Cleaner class so that we don't get into trouble later in 133 // the run loop if there's memory shortage while loading/initializing them lazily. 134 ensureClassInitialized(Cleaner.class); 135 } 136 137 ReferenceHandler(ThreadGroup g, String name) { 138 super(g, null, name, 0, false); 139 } 140 141 public void run() { 142 while (true) { 143 tryHandlePending(true); 144 } 145 } 146 } 147 148 /** 149 * Try handle pending {@link Reference} if there is one.<p> 150 * Return {@code true} as a hint that there might be another 151 * {@link Reference} pending or {@code false} when there are no more pending 152 * {@link Reference}s at the moment and the program can do some other 153 * useful work instead of looping. 154 * 155 * @param waitForNotify if {@code true} and there was no pending 156 * {@link Reference}, wait until notified from VM 157 * or interrupted; if {@code false}, return immediately 158 * when there is no pending {@link Reference}. 159 * @return {@code true} if there was a {@link Reference} pending and it 160 * was processed, or we waited for notification and either got it 161 * or thread was interrupted before being notified; 162 * {@code false} otherwise. 163 */ 164 static boolean tryHandlePending(boolean waitForNotify) { 165 if (!waitForNotify) { 166 // FIXME! Calls from non-blocking helper threads not yet supported. 167 return false; 168 } 169 170 // Get pending references from the VM 171 Reference<Object> pending_list = getReferencePendingList(); 172 173 // Enqueue references 174 while (pending_list != null) { 175 // Unlink 176 Reference<Object> r = pending_list; 177 pending_list = r.discovered; 178 r.discovered = null; 179 180 // Fast path for cleaners 181 if (r instanceof Cleaner) { 182 try { 183 ((Cleaner)r).clean(); 184 } catch (Throwable x) { 185 // Ignored 186 } 187 188 // Don't enqueue 189 continue; 190 } 191 192 // Enqueue 193 ReferenceQueue<? super Object> q = r.queue; 194 if (q != ReferenceQueue.NULL) { 195 q.enqueue(r); 196 } 197 } 198 199 return true; 200 } 201 202 static { 203 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 204 for (ThreadGroup tgn = tg; 205 tgn != null; 206 tg = tgn, tgn = tg.getParent()); 207 Thread handler = new ReferenceHandler(tg, "Reference Handler"); 208 /* If there were a special system-only priority greater than 209 * MAX_PRIORITY, it would be used here 210 */ 211 handler.setPriority(Thread.MAX_PRIORITY); 212 handler.setDaemon(true); 213 handler.start(); 214 215 // provide access in SharedSecrets 216 SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { 217 @Override 218 public boolean tryHandlePendingReference() { |