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