1 /* 2 * Copyright (C) 2007 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.base; 18 19 import com.google.common.annotations.VisibleForTesting; 20 21 import java.io.Closeable; 22 import java.io.FileNotFoundException; 23 import java.io.IOException; 24 import java.lang.ref.PhantomReference; 25 import java.lang.ref.Reference; 26 import java.lang.ref.ReferenceQueue; 27 import java.lang.reflect.Method; 28 import java.net.URL; 29 import java.net.URLClassLoader; 30 import java.util.logging.Level; 31 import java.util.logging.Logger; 32 33 /** 34 * A reference queue with an associated background thread that dequeues references and invokes 35 * {@link FinalizableReference#finalizeReferent()} on them. 36 * 37 * <p>Keep a strong reference to this object until all of the associated referents have been 38 * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code 39 * finalizeReferent()} on the remaining references. 40 * 41 * <p>As an example of how this is used, imagine you have a class {@code MyServer} that creates a 42 * a {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the 43 * {@code ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without 44 * calling its {@code close} method. You <em>could</em> use a finalizer to accomplish this, but 45 * that has a number of well-known problems. Here is how you might use this class instead: 46 * 47 * <pre> 48 * public class MyServer implements Closeable { 49 * private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue(); 50 * // You might also share this between several objects. 51 * 52 * private static final Set<Reference<?>> references = Sets.newConcurrentHashSet(); 53 * // This ensures that the FinalizablePhantomReference itself is not garbage-collected. 54 * 55 * private final ServerSocket serverSocket; 56 * 57 * private MyServer(...) { 58 * ... 59 * this.serverSocket = new ServerSocket(...); 60 * ... 61 * } 62 * 63 * public static MyServer create(...) { 64 * MyServer myServer = new MyServer(...); 65 * final ServerSocket serverSocket = myServer.serverSocket; 66 * Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) { 67 * @Override public void finalizeReferent() { 68 * references.remove(this): 69 * if (!serverSocket.isClosed()) { 70 * ...log a message about how nobody called close()... 71 * try { 72 * serverSocket.close(); 73 * } catch (IOException e) { 74 * ... 75 * } 76 * } 77 * } 78 * }; 79 * references.add(reference); 80 * return myServer; 81 * } 82 * 83 * @Override public void close() { 84 * serverSocket.close(); 85 * } 86 * } 87 * </pre> 88 * 89 * @author Bob Lee 90 * @since 2.0 (imported from Google Collections Library) 91 */ 92 public class FinalizableReferenceQueue implements Closeable { 93 /* 94 * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a 95 * map built by MapMaker) no longer has a strong reference to this object, the garbage collector 96 * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the 97 * Finalizer to stop. 98 * 99 * If this library is loaded in the system class loader, FinalizableReferenceQueue can load 100 * Finalizer directly with no problems. 101 * 102 * If this library is loaded in an application class loader, it's important that Finalizer not 103 * have a strong reference back to the class loader. Otherwise, you could have a graph like this: 104 * 105 * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader 106 * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance 107 * 108 * Even if no other references to classes from the application class loader remain, the Finalizer 109 * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the 110 * Finalizer running, and as a result, the application class loader can never be reclaimed. 111 * 112 * This means that dynamically loaded web applications and OSGi bundles can't be unloaded. 113 * 114 * If the library is loaded in an application class loader, we try to break the cycle by loading 115 * Finalizer in its own independent class loader: 116 * 117 * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue 118 * -> etc. -> Decoupled class loader -> Finalizer 119 * 120 * Now, Finalizer no longer keeps an indirect strong reference to the static 121 * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed 122 * at which point the Finalizer thread will stop and its decoupled class loader can also be 123 * reclaimed. 124 * 125 * If any of this fails along the way, we fall back to loading Finalizer directly in the 126 * application class loader. 127 */ 128 129 private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); 130 131 private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer"; 132 133 /** Reference to Finalizer.startFinalizer(). */ 134 private static final Method startFinalizer; 135 static { 136 Class<?> finalizer = loadFinalizer( 137 new SystemLoader(), new DecoupledLoader(), new DirectLoader()); 138 startFinalizer = getStartFinalizer(finalizer); 139 } 140 141 /** 142 * The actual reference queue that our background thread will poll. 143 */ 144 final ReferenceQueue<Object> queue; 145 146 final PhantomReference<Object> frqRef; 147 148 /** 149 * Whether or not the background thread started successfully. 150 */ 151 final boolean threadStarted; 152 153 /** 154 * Constructs a new queue. 155 */ 156 public FinalizableReferenceQueue() { 157 // We could start the finalizer lazily, but I'd rather it blow up early. 158 queue = new ReferenceQueue<Object>(); 159 frqRef = new PhantomReference<Object>(this, queue); 160 boolean threadStarted = false; 161 try { 162 startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); 163 threadStarted = true; 164 } catch (IllegalAccessException impossible) { 165 throw new AssertionError(impossible); // startFinalizer() is public 166 } catch (Throwable t) { 167 logger.log(Level.INFO, "Failed to start reference finalizer thread." 168 + " Reference cleanup will only occur when new references are created.", t); 169 } 170 171 this.threadStarted = threadStarted; 172 } 173 174 @Override 175 public void close() { 176 frqRef.enqueue(); 177 cleanUp(); 178 } 179 180 /** 181 * Repeatedly dequeues references from the queue and invokes {@link 182 * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a 183 * no-op if the background thread was created successfully. 184 */ 185 void cleanUp() { 186 if (threadStarted) { 187 return; 188 } 189 190 Reference<?> reference; 191 while ((reference = queue.poll()) != null) { 192 /* 193 * This is for the benefit of phantom references. Weak and soft references will have already 194 * been cleared by this point. 195 */ 196 reference.clear(); 197 try { 198 ((FinalizableReference) reference).finalizeReferent(); 199 } catch (Throwable t) { 200 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 201 } 202 } 203 } 204 205 /** 206 * Iterates through the given loaders until it finds one that can load Finalizer. 207 * 208 * @return Finalizer.class 209 */ 210 private static Class<?> loadFinalizer(FinalizerLoader... loaders) { 211 for (FinalizerLoader loader : loaders) { 212 Class<?> finalizer = loader.loadFinalizer(); 213 if (finalizer != null) { 214 return finalizer; 215 } 216 } 217 218 throw new AssertionError(); 219 } 220 221 /** 222 * Loads Finalizer.class. 223 */ 224 interface FinalizerLoader { 225 226 /** 227 * Returns Finalizer.class or null if this loader shouldn't or can't load it. 228 * 229 * @throws SecurityException if we don't have the appropriate privileges 230 */ 231 Class<?> loadFinalizer(); 232 } 233 234 /** 235 * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path, 236 * we needn't create a separate loader. 237 */ 238 static class SystemLoader implements FinalizerLoader { 239 // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable 240 // finding Finalizer on the system class path even if it is there. 241 @VisibleForTesting 242 static boolean disabled; 243 244 @Override 245 public Class<?> loadFinalizer() { 246 if (disabled) { 247 return null; 248 } 249 ClassLoader systemLoader; 250 try { 251 systemLoader = ClassLoader.getSystemClassLoader(); 252 } catch (SecurityException e) { 253 logger.info("Not allowed to access system class loader."); 254 return null; 255 } 256 if (systemLoader != null) { 257 try { 258 return systemLoader.loadClass(FINALIZER_CLASS_NAME); 259 } catch (ClassNotFoundException e) { 260 // Ignore. Finalizer is simply in a child class loader. 261 return null; 262 } 263 } else { 264 return null; 265 } 266 } 267 } 268 269 /** 270 * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to 271 * our class loader (which could be that of a dynamically loaded web application or OSGi bundle), 272 * it would prevent our class loader from getting garbage collected. 273 */ 274 static class DecoupledLoader implements FinalizerLoader { 275 private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader." 276 + "Loading Finalizer in the current class loader instead. As a result, you will not be able" 277 + "to garbage collect this class loader. To support reclaiming this class loader, either" 278 + "resolve the underlying issue, or move Google Collections to your system class path."; 279 280 @Override 281 public Class<?> loadFinalizer() { 282 try { 283 /* 284 * We use URLClassLoader because it's the only concrete class loader implementation in the 285 * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this 286 * class loader: 287 * 288 * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader 289 * 290 * System class loader will (and must) be the parent. 291 */ 292 ClassLoader finalizerLoader = newLoader(getBaseUrl()); 293 return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); 294 } catch (Exception e) { 295 logger.log(Level.WARNING, LOADING_ERROR, e); 296 return null; 297 } 298 } 299 300 /** 301 * Gets URL for base of path containing Finalizer.class. 302 */ 303 URL getBaseUrl() throws IOException { 304 // Find URL pointing to Finalizer.class file. 305 String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; 306 URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); 307 if (finalizerUrl == null) { 308 throw new FileNotFoundException(finalizerPath); 309 } 310 311 // Find URL pointing to base of class path. 312 String urlString = finalizerUrl.toString(); 313 if (!urlString.endsWith(finalizerPath)) { 314 throw new IOException("Unsupported path style: " + urlString); 315 } 316 urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); 317 return new URL(finalizerUrl, urlString); 318 } 319 320 /** Creates a class loader with the given base URL as its classpath. */ 321 URLClassLoader newLoader(URL base) { 322 // We use the bootstrap class loader as the parent because Finalizer by design uses 323 // only standard Java classes. That also means that FinalizableReferenceQueueTest 324 // doesn't pick up the wrong version of the Finalizer class. 325 return new URLClassLoader(new URL[] {base}, null); 326 } 327 } 328 329 /** 330 * Loads Finalizer directly using the current class loader. We won't be able to garbage collect 331 * this class loader, but at least the world doesn't end. 332 */ 333 static class DirectLoader implements FinalizerLoader { 334 @Override 335 public Class<?> loadFinalizer() { 336 try { 337 return Class.forName(FINALIZER_CLASS_NAME); 338 } catch (ClassNotFoundException e) { 339 throw new AssertionError(e); 340 } 341 } 342 } 343 344 /** 345 * Looks up Finalizer.startFinalizer(). 346 */ 347 static Method getStartFinalizer(Class<?> finalizer) { 348 try { 349 return finalizer.getMethod( 350 "startFinalizer", 351 Class.class, 352 ReferenceQueue.class, 353 PhantomReference.class); 354 } catch (NoSuchMethodException e) { 355 throw new AssertionError(e); 356 } 357 } 358 }