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