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 }