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