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