001/* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.base; 016 017import com.google.common.annotations.GwtIncompatible; 018import com.google.common.annotations.J2ktIncompatible; 019import com.google.common.annotations.VisibleForTesting; 020import java.io.Closeable; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.lang.ref.PhantomReference; 024import java.lang.ref.Reference; 025import java.lang.ref.ReferenceQueue; 026import java.lang.reflect.Method; 027import java.net.URL; 028import java.net.URLClassLoader; 029import java.util.logging.Level; 030import java.util.logging.Logger; 031import javax.annotation.CheckForNull; 032 033/** 034 * A reference queue with an associated background thread that dequeues references and invokes 035 * {@link FinalizableReference#finalizeReferent()} on them. Java 9+ users should prefer {@link 036 * java.lang.ref.Cleaner Cleaner}; see example <a href="#cleaner">below</a>. 037 * 038 * <p>Keep a strong reference to this object until all of the associated referents have been 039 * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code 040 * finalizeReferent()} on the remaining references. 041 * 042 * <p>As an example of how this is used, imagine you have a class {@code MyServer} that creates a 043 * {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the {@code 044 * ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without calling 045 * its {@code close} method. You <em>could</em> use a finalizer to accomplish this, but that has a 046 * number of well-known problems. Here is how you might use this class instead: 047 * 048 * <pre>{@code 049 * public class MyServer implements Closeable { 050 * private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue(); 051 * // You might also share this between several objects. 052 * 053 * private static final Set<Reference<?>> references = Sets.newConcurrentHashSet(); 054 * // This ensures that the FinalizablePhantomReference itself is not garbage-collected. 055 * 056 * private final ServerSocket serverSocket; 057 * 058 * private MyServer(...) { 059 * ... 060 * this.serverSocket = new ServerSocket(...); 061 * ... 062 * } 063 * 064 * public static MyServer create(...) { 065 * MyServer myServer = new MyServer(...); 066 * ServerSocket serverSocket = myServer.serverSocket; 067 * Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) { 068 * @Override 069 * public void finalizeReferent() { 070 * references.remove(this): 071 * if (!serverSocket.isClosed()) { 072 * ...log a message about how nobody called close()... 073 * try { 074 * serverSocket.close(); 075 * } catch (IOException e) { 076 * ... 077 * } 078 * } 079 * } 080 * }; 081 * references.add(reference); 082 * return myServer; 083 * } 084 * 085 * @Override 086 * public void close() throws IOException { 087 * serverSocket.close(); 088 * } 089 * } 090 * }</pre> 091 * 092 * <p id="cleaner">Here is how you might achieve the same thing using {@link java.lang.ref.Cleaner 093 * Cleaner}, if you are using a Java version where that is available: 094 * 095 * <pre>{@code 096 * public class MyServer implements Closeable { 097 * private static final Cleaner cleaner = Cleaner.create(); 098 * // You might also share this between several objects. 099 * 100 * private final ServerSocket serverSocket; 101 * private final Cleaner.Cleanable cleanable; 102 * 103 * public MyServer(...) { 104 * ... 105 * this.serverSocket = new ServerSocket(...); 106 * this.cleanable = cleaner.register(this, closeServerSocketRunnable(serverSocket)); 107 * ... 108 * } 109 * 110 * private static Runnable closeServerSocketRunnable(ServerSocket serverSocket) { 111 * return () -> { 112 * if (!serverSocket.isClosed()) { 113 * ...log a message about how nobody called close()... 114 * try { 115 * serverSocket.close(); 116 * } catch (IOException e) { 117 * ... 118 * } 119 * } 120 * }; 121 * } 122 * 123 * @Override 124 * public void close() throws IOException { 125 * serverSocket.close(); 126 * cleanable.clean(); 127 * } 128 * } 129 * }</pre> 130 * 131 * <p>Some care is needed when using {@code Cleaner} to ensure that the callback passed to {@code 132 * register} does not have a reference to the object (in this case, {@code MyServer}) that may be 133 * garbage-collected. That's why we are careful to make a {@code Runnable} that does not have a 134 * reference to any {@code MyServer} instance. 135 * 136 * @author Bob Lee 137 * @since 2.0 138 */ 139@J2ktIncompatible 140@GwtIncompatible 141@ElementTypesAreNonnullByDefault 142public class FinalizableReferenceQueue implements Closeable { 143 /* 144 * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a 145 * map built by MapMaker) no longer has a strong reference to this object, the garbage collector 146 * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the 147 * Finalizer to stop. 148 * 149 * If this library is loaded in the system class loader, FinalizableReferenceQueue can load 150 * Finalizer directly with no problems. 151 * 152 * If this library is loaded in an application class loader, it's important that Finalizer not 153 * have a strong reference back to the class loader. Otherwise, you could have a graph like this: 154 * 155 * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader 156 * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance 157 * 158 * Even if no other references to classes from the application class loader remain, the Finalizer 159 * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the 160 * Finalizer running, and as a result, the application class loader can never be reclaimed. 161 * 162 * This means that dynamically loaded web applications and OSGi bundles can't be unloaded. 163 * 164 * If the library is loaded in an application class loader, we try to break the cycle by loading 165 * Finalizer in its own independent class loader: 166 * 167 * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue -> 168 * etc. -> Decoupled class loader -> Finalizer 169 * 170 * Now, Finalizer no longer keeps an indirect strong reference to the static 171 * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed 172 * at which point the Finalizer thread will stop and its decoupled class loader can also be 173 * reclaimed. 174 * 175 * If any of this fails along the way, we fall back to loading Finalizer directly in the 176 * application class loader. 177 * 178 * NOTE: The tests for this behavior (FinalizableReferenceQueueClassLoaderUnloadingTest) fail 179 * strangely when run in JDK 9. We are considering this a known issue. Please see 180 * https://github.com/google/guava/issues/3086 for more information. 181 */ 182 183 private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); 184 185 private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer"; 186 187 /** Reference to Finalizer.startFinalizer(). */ 188 private static final Method startFinalizer; 189 190 static { 191 Class<?> finalizer = 192 loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader()); 193 startFinalizer = getStartFinalizer(finalizer); 194 } 195 196 /** The actual reference queue that our background thread will poll. */ 197 final ReferenceQueue<Object> queue; 198 199 final PhantomReference<Object> frqRef; 200 201 /** Whether or not the background thread started successfully. */ 202 final boolean threadStarted; 203 204 /** Constructs a new queue. */ 205 public FinalizableReferenceQueue() { 206 // We could start the finalizer lazily, but I'd rather it blow up early. 207 queue = new ReferenceQueue<>(); 208 frqRef = new PhantomReference<>(this, queue); 209 boolean threadStarted = false; 210 try { 211 startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); 212 threadStarted = true; 213 } catch (IllegalAccessException impossible) { 214 throw new AssertionError(impossible); // startFinalizer() is public 215 } catch (Throwable t) { 216 logger.log( 217 Level.INFO, 218 "Failed to start reference finalizer thread." 219 + " Reference cleanup will only occur when new references are created.", 220 t); 221 } 222 223 this.threadStarted = threadStarted; 224 } 225 226 @Override 227 public void close() { 228 frqRef.enqueue(); 229 cleanUp(); 230 } 231 232 /** 233 * Repeatedly dequeues references from the queue and invokes {@link 234 * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a 235 * no-op if the background thread was created successfully. 236 */ 237 void cleanUp() { 238 if (threadStarted) { 239 return; 240 } 241 242 Reference<?> reference; 243 while ((reference = queue.poll()) != null) { 244 /* 245 * This is for the benefit of phantom references. Weak and soft references will have already 246 * been cleared by this point. 247 */ 248 reference.clear(); 249 try { 250 ((FinalizableReference) reference).finalizeReferent(); 251 } catch (Throwable t) { 252 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 253 } 254 } 255 } 256 257 /** 258 * Iterates through the given loaders until it finds one that can load Finalizer. 259 * 260 * @return Finalizer.class 261 */ 262 private static Class<?> loadFinalizer(FinalizerLoader... loaders) { 263 for (FinalizerLoader loader : loaders) { 264 Class<?> finalizer = loader.loadFinalizer(); 265 if (finalizer != null) { 266 return finalizer; 267 } 268 } 269 270 throw new AssertionError(); 271 } 272 273 /** Loads Finalizer.class. */ 274 interface FinalizerLoader { 275 276 /** 277 * Returns Finalizer.class or null if this loader shouldn't or can't load it. 278 * 279 * @throws SecurityException if we don't have the appropriate privileges 280 */ 281 @CheckForNull 282 Class<?> loadFinalizer(); 283 } 284 285 /** 286 * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path, 287 * we needn't create a separate loader. 288 */ 289 static class SystemLoader implements FinalizerLoader { 290 // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable 291 // finding Finalizer on the system class path even if it is there. 292 @VisibleForTesting static boolean disabled; 293 294 @Override 295 @CheckForNull 296 public Class<?> loadFinalizer() { 297 if (disabled) { 298 return null; 299 } 300 ClassLoader systemLoader; 301 try { 302 systemLoader = ClassLoader.getSystemClassLoader(); 303 } catch (SecurityException e) { 304 logger.info("Not allowed to access system class loader."); 305 return null; 306 } 307 if (systemLoader != null) { 308 try { 309 return systemLoader.loadClass(FINALIZER_CLASS_NAME); 310 } catch (ClassNotFoundException e) { 311 // Ignore. Finalizer is simply in a child class loader. 312 return null; 313 } 314 } else { 315 return null; 316 } 317 } 318 } 319 320 /** 321 * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to 322 * our class loader (which could be that of a dynamically loaded web application or OSGi bundle), 323 * it would prevent our class loader from getting garbage collected. 324 */ 325 static class DecoupledLoader implements FinalizerLoader { 326 private static final String LOADING_ERROR = 327 "Could not load Finalizer in its own class loader. Loading Finalizer in the current class " 328 + "loader instead. As a result, you will not be able to garbage collect this class " 329 + "loader. To support reclaiming this class loader, either resolve the underlying " 330 + "issue, or move Guava to your system class path."; 331 332 @Override 333 @CheckForNull 334 public Class<?> loadFinalizer() { 335 /* 336 * We use URLClassLoader because it's the only concrete class loader implementation in the 337 * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this 338 * class loader: 339 * 340 * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader 341 * 342 * System class loader will (and must) be the parent. 343 */ 344 try (URLClassLoader finalizerLoader = newLoader(getBaseUrl())) { 345 return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); 346 } catch (Exception e) { 347 logger.log(Level.WARNING, LOADING_ERROR, e); 348 return null; 349 } 350 } 351 352 /** Gets URL for base of path containing Finalizer.class. */ 353 URL getBaseUrl() throws IOException { 354 // Find URL pointing to Finalizer.class file. 355 String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; 356 URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); 357 if (finalizerUrl == null) { 358 throw new FileNotFoundException(finalizerPath); 359 } 360 361 // Find URL pointing to base of class path. 362 String urlString = finalizerUrl.toString(); 363 if (!urlString.endsWith(finalizerPath)) { 364 throw new IOException("Unsupported path style: " + urlString); 365 } 366 urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); 367 return new URL(finalizerUrl, urlString); 368 } 369 370 /** Creates a class loader with the given base URL as its classpath. */ 371 URLClassLoader newLoader(URL base) { 372 // We use the bootstrap class loader as the parent because Finalizer by design uses 373 // only standard Java classes. That also means that FinalizableReferenceQueueTest 374 // doesn't pick up the wrong version of the Finalizer class. 375 return new URLClassLoader(new URL[] {base}, null); 376 } 377 } 378 379 /** 380 * Loads Finalizer directly using the current class loader. We won't be able to garbage collect 381 * this class loader, but at least the world doesn't end. 382 */ 383 static class DirectLoader implements FinalizerLoader { 384 @Override 385 public Class<?> loadFinalizer() { 386 try { 387 return Class.forName(FINALIZER_CLASS_NAME); 388 } catch (ClassNotFoundException e) { 389 throw new AssertionError(e); 390 } 391 } 392 } 393 394 /** Looks up Finalizer.startFinalizer(). */ 395 static Method getStartFinalizer(Class<?> finalizer) { 396 try { 397 return finalizer.getMethod( 398 "startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class); 399 } catch (NoSuchMethodException e) { 400 throw new AssertionError(e); 401 } 402 } 403}