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