1 /* 2 * Copyright (c) 2015, 2016, 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 jdk.internal.ref; 27 28 import java.lang.ref.Cleaner; 29 import java.lang.ref.Cleaner.Cleanable; 30 import java.lang.ref.ReferenceQueue; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.concurrent.ThreadFactory; 34 import java.util.function.Function; 35 36 import sun.misc.InnocuousThread; 37 38 /** 39 * CleanerImpl manages a set of object references and corresponding cleaning actions. 40 * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}. 41 */ 42 public final class CleanerImpl { 43 44 /** 45 * An object to access the CleanerImpl from a Cleaner; set by Cleaner init. 46 */ 47 private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null; 48 49 /** 50 * Heads of a CleanableList for each reference type. 51 */ 52 final PhantomCleanable<?> phantomCleanableList; 53 54 final WeakCleanable<?> weakCleanableList; 55 56 final SoftCleanable<?> softCleanableList; 57 58 // The ReferenceQueue of pending cleaning actions 59 final ReferenceQueue<Object> queue; 60 61 /** 62 * Called by Cleaner static initialization to provide the function 63 * to map from Cleaner to CleanerImpl. 64 * @param access a function to map from Cleaner to CleanerImpl 65 */ 66 public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) { 67 if (cleanerImplAccess == null) { 68 cleanerImplAccess = access; 69 } else { 70 throw new InternalError("cleanerImplAccess"); 71 } 72 } 73 74 /** 75 * Called to get the CleanerImpl for a Cleaner. 76 * @param cleaner the cleaner 77 * @return the corresponding CleanerImpl 78 */ 79 public static CleanerImpl getCleanerImpl(Cleaner cleaner) { 80 return cleanerImplAccess.apply(cleaner); 81 } 82 83 /** 84 * Constructor for CleanerImpl. 85 */ 86 public CleanerImpl() { 87 queue = new ReferenceQueue<>(); 88 phantomCleanableList = new PhantomCleanableRef(); 89 weakCleanableList = new WeakCleanableRef(); 90 softCleanableList = new SoftCleanableRef(); 91 } 92 93 /** 94 * Starts the Cleaner implementation. 95 * Ensure this is the CleanerImpl for the Cleaner. 96 * When started waits for Cleanables to be queued. 97 * @param cleaner the cleaner 98 * @param threadFactory the thread factory 99 */ 100 public void start(Cleaner cleaner, ThreadFactory threadFactory) { 101 if (getCleanerImpl(cleaner) != this) { 102 throw new AssertionError("wrong cleaner"); 103 } 104 // schedule a nop cleaning action for the cleaner, so the associated thread 105 // will continue to run at least until the cleaner is reclaimable. 106 new PhantomCleanableRef(cleaner, cleaner, () -> {}); 107 108 if (threadFactory == null) { 109 threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); 110 } 111 112 // now that there's at least one cleaning action, for the cleaner, 113 // we can start the associated thread, which runs until 114 // all cleaning actions have been run. 115 Thread thread = threadFactory.newThread(this::run); 116 thread.setDaemon(true); 117 thread.start(); 118 } 119 120 /** 121 * Process queued Cleanables as long as the cleanable lists are not empty. 122 * A Cleanable is in one of the lists for each Object and for the Cleaner 123 * itself. 124 * Terminates when the Cleaner is no longer reachable and 125 * has been cleaned and there are no more Cleanable instances 126 * for which the object is reachable. 127 * <p> 128 * If the thread is a ManagedLocalsThread, the threadlocals 129 * are erased before each cleanup 130 */ 131 private void run() { 132 Thread t = Thread.currentThread(); 133 InnocuousThread mlThread = (t instanceof InnocuousThread) 134 ? (InnocuousThread) t 135 : null; 136 while (!phantomCleanableList.isListEmpty() || 137 !weakCleanableList.isListEmpty() || 138 !softCleanableList.isListEmpty()) { 139 if (mlThread != null) { 140 // Clear the thread locals 141 mlThread.eraseThreadLocals(); 142 } 143 try { 144 // Wait for a Ref, with a timeout to avoid getting hung 145 // due to a race with clear/clean 146 Cleanable ref = (Cleanable) queue.remove(60 * 1000L); 147 if (ref != null) { 148 ref.clean(); 149 } 150 } catch (Throwable e) { 151 // ignore exceptions from the cleanup action 152 // (including interruption of cleanup thread) 153 } 154 } 155 } 156 157 /** 158 * Processes all Cleanable(s) that have been waiting in the queue. 159 * 160 * @return {@code true} if any Cleanable was found in the queue and 161 * was processed or {@code false} if the queue was empty. 162 */ 163 public boolean drainQueue() { 164 boolean cleaned = false; 165 Cleanable ref; 166 while ((ref = (Cleanable) queue.poll()) != null) { 167 try { 168 ref.clean(); 169 } catch (Throwable t) { 170 // ignore exceptions from the cleanup action 171 } 172 cleaned = true; 173 } 174 return cleaned; 175 } 176 177 /** 178 * Perform cleaning on an unreachable PhantomReference. 179 */ 180 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 181 private final Runnable action; 182 183 /** 184 * Constructor for a phantom cleanable reference. 185 * @param obj the object to monitor 186 * @param cleaner the cleaner 187 * @param action the action Runnable 188 */ 189 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 190 super(obj, cleaner); 191 this.action = action; 192 } 193 194 /** 195 * Constructor used only for root of phantom cleanable list. 196 */ 197 PhantomCleanableRef() { 198 super(); 199 this.action = null; 200 } 201 202 @Override 203 protected void performCleanup() { 204 action.run(); 205 } 206 207 /** 208 * Prevent access to referent even when it is still alive. 209 * 210 * @throws UnsupportedOperationException always 211 */ 212 @Override 213 public Object get() { 214 throw new UnsupportedOperationException("get"); 215 } 216 217 /** 218 * Direct clearing of the referent is not supported. 219 * 220 * @throws UnsupportedOperationException always 221 */ 222 @Override 223 public void clear() { 224 throw new UnsupportedOperationException("clear"); 225 } 226 } 227 228 /** 229 * Perform cleaning on an unreachable WeakReference. 230 */ 231 public static final class WeakCleanableRef extends WeakCleanable<Object> { 232 private final Runnable action; 233 234 /** 235 * Constructor for a weak cleanable reference. 236 * @param obj the object to monitor 237 * @param cleaner the cleaner 238 * @param action the action Runnable 239 */ 240 WeakCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 241 super(obj, cleaner); 242 this.action = action; 243 } 244 245 /** 246 * Constructor used only for root of weak cleanable list. 247 */ 248 WeakCleanableRef() { 249 super(); 250 this.action = null; 251 } 252 253 @Override 254 protected void performCleanup() { 255 action.run(); 256 } 257 258 /** 259 * Prevent access to referent even when it is still alive. 260 * 261 * @throws UnsupportedOperationException always 262 */ 263 @Override 264 public Object get() { 265 throw new UnsupportedOperationException("get"); 266 } 267 268 /** 269 * Direct clearing of the referent is not supported. 270 * 271 * @throws UnsupportedOperationException always 272 */ 273 @Override 274 public void clear() { 275 throw new UnsupportedOperationException("clear"); 276 } 277 } 278 279 /** 280 * Perform cleaning on an unreachable SoftReference. 281 */ 282 public static final class SoftCleanableRef extends SoftCleanable<Object> { 283 private final Runnable action; 284 285 /** 286 * Constructor for a soft cleanable reference. 287 * @param obj the object to monitor 288 * @param cleaner the cleaner 289 * @param action the action Runnable 290 */ 291 SoftCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 292 super(obj, cleaner); 293 this.action = action; 294 } 295 296 /** 297 * Constructor used only for root of soft cleanable list. 298 */ 299 SoftCleanableRef() { 300 super(); 301 this.action = null; 302 } 303 304 @Override 305 protected void performCleanup() { 306 action.run(); 307 } 308 309 /** 310 * Prevent access to referent even when it is still alive. 311 * 312 * @throws UnsupportedOperationException always 313 */ 314 @Override 315 public Object get() { 316 throw new UnsupportedOperationException("get"); 317 } 318 319 /** 320 * Direct clearing of the referent is not supported. 321 * 322 * @throws UnsupportedOperationException always 323 */ 324 @Override 325 public void clear() { 326 throw new UnsupportedOperationException("clear"); 327 } 328 329 } 330 331 /** 332 * A ThreadFactory for InnocuousThreads. 333 * The factory is a singleton. 334 */ 335 static final class InnocuousThreadFactory implements ThreadFactory { 336 final static ThreadFactory factory = new InnocuousThreadFactory(); 337 338 static ThreadFactory factory() { 339 return factory; 340 } 341 342 public Thread newThread(Runnable r) { 343 return AccessController.doPrivileged((PrivilegedAction<Thread>) () -> { 344 Thread t = new InnocuousThread(r); 345 t.setPriority(Thread.MAX_PRIORITY - 2); 346 t.setName("Cleaner-" + t.getId()); 347 return t; 348 }); 349 } 350 } 351 352 }