156 } 157 158 /** 159 * Try handle pending {@link Reference} if there is one.<p> 160 * Return {@code true} as a hint that there might be another 161 * {@link Reference} pending or {@code false} when there are no more pending 162 * {@link Reference}s at the moment and the program can do some other 163 * useful work instead of looping. 164 * 165 * @param waitForNotify if {@code true} and there was no pending 166 * {@link Reference}, wait until notified from VM 167 * or interrupted; if {@code false}, return immediately 168 * when there is no pending {@link Reference}. 169 * @return {@code true} if there was a {@link Reference} pending and it 170 * was processed, or we waited for notification and either got it 171 * or thread was interrupted before being notified; 172 * {@code false} otherwise. 173 */ 174 static boolean tryHandlePending(boolean waitForNotify) { 175 Reference<Object> r; 176 Cleaner c; 177 try { 178 synchronized (lock) { 179 if (pending != null) { 180 r = pending; 181 // 'instanceof' might throw OutOfMemoryError sometimes 182 // so do this before un-linking 'r' from the 'pending' chain... 183 c = r instanceof Cleaner ? (Cleaner) r : null; 184 // unlink 'r' from 'pending' chain 185 pending = r.discovered; 186 r.discovered = null; 187 } else { 188 // The waiting on the lock may cause an OutOfMemoryError 189 // because it may try to allocate exception objects. 190 if (waitForNotify) { 191 lock.wait(); 192 } 193 // retry if waited 194 return waitForNotify; 195 } 196 } 197 } catch (OutOfMemoryError x) { 198 // Give other threads CPU time so they hopefully drop some live references 199 // and GC reclaims some space. 200 // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above 201 // persistently throws OOME for some time... 202 Thread.yield(); 203 // retry 204 return true; 205 } catch (InterruptedException x) { 206 // retry 207 return true; 208 } 209 210 // Fast path for cleaners 211 if (c != null) { 212 c.clean(); 213 return true; 214 } 215 216 ReferenceQueue<? super Object> q = r.queue; 217 if (q != ReferenceQueue.NULL) q.enqueue(r); 218 return true; 219 } 220 221 static { 222 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 223 for (ThreadGroup tgn = tg; 224 tgn != null; 225 tg = tgn, tgn = tg.getParent()); | 156 } 157 158 /** 159 * Try handle pending {@link Reference} if there is one.<p> 160 * Return {@code true} as a hint that there might be another 161 * {@link Reference} pending or {@code false} when there are no more pending 162 * {@link Reference}s at the moment and the program can do some other 163 * useful work instead of looping. 164 * 165 * @param waitForNotify if {@code true} and there was no pending 166 * {@link Reference}, wait until notified from VM 167 * or interrupted; if {@code false}, return immediately 168 * when there is no pending {@link Reference}. 169 * @return {@code true} if there was a {@link Reference} pending and it 170 * was processed, or we waited for notification and either got it 171 * or thread was interrupted before being notified; 172 * {@code false} otherwise. 173 */ 174 static boolean tryHandlePending(boolean waitForNotify) { 175 Reference<Object> r; 176 try { 177 synchronized (lock) { 178 if (pending != null) { 179 r = pending; 180 // unlink 'r' from 'pending' chain 181 pending = r.discovered; 182 r.discovered = null; 183 } else { 184 // The waiting on the lock may cause an OutOfMemoryError 185 // because it may try to allocate exception objects. 186 if (waitForNotify) { 187 lock.wait(); 188 } 189 // retry if waited 190 return waitForNotify; 191 } 192 } 193 } catch (OutOfMemoryError x) { 194 // Give other threads CPU time so they hopefully drop some live references 195 // and GC reclaims some space. 196 Thread.yield(); 197 // retry 198 return true; 199 } catch (InterruptedException x) { 200 // retry 201 return true; 202 } 203 204 Cleaner c; 205 try { 206 // It was established that "instanceof Cleaner" can cause OOME 207 // to be thrown. The only logical explanation is that this is caused 208 // by unsuccessful loading of the Cleaner class, triggered by instanceof. 209 // So now we pre-load Cleaner class at Reference class initialization 210 // time, but we are superstitious and still catch OOME here. 211 c = (r instanceof Cleaner) ? (Cleaner) r : null; 212 } catch (OutOfMemoryError x) { 213 // Give other threads CPU time so they hopefully drop some live references 214 // and GC reclaims some space. 215 Thread.yield(); 216 // re-link 'r' back to 'pending' chain 217 synchronized (lock) { 218 r.discovered = pending; 219 pending = r; 220 } 221 // retry 222 return true; 223 } 224 225 // Fast path for cleaners 226 if (c != null) { 227 c.clean(); 228 return true; 229 } 230 231 ReferenceQueue<? super Object> q = r.queue; 232 if (q != ReferenceQueue.NULL) q.enqueue(r); 233 return true; 234 } 235 236 static { 237 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 238 for (ThreadGroup tgn = tg; 239 tgn != null; 240 tg = tgn, tgn = tg.getParent()); |