001 /*
002 * Copyright (C) 2008 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.internal;
018
019 import java.lang.ref.PhantomReference;
020 import java.lang.ref.Reference;
021 import java.lang.ref.ReferenceQueue;
022 import java.lang.ref.WeakReference;
023 import java.lang.reflect.Field;
024 import java.lang.reflect.Method;
025 import java.util.logging.Level;
026 import java.util.logging.Logger;
027
028 /**
029 * Thread that finalizes referents. All references should implement
030 * {@code com.google.common.base.FinalizableReference}.
031 *
032 * <p>While this class is public, we consider it to be *internal* and not part
033 * of our published API. It is public so we can access it reflectively across
034 * class loaders in secure environments.
035 *
036 * <p>This class can't depend on other Google Collections code. If we were
037 * to load this class in the same class loader as the rest of
038 * Google Collections, this thread would keep an indirect strong reference
039 * to the class loader and prevent it from being garbage collected. This
040 * poses a problem for environments where you want to throw away the class
041 * loader. For example, dynamically reloading a web application or unloading
042 * an OSGi bundle.
043 *
044 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
045 * in its own class loader. That way, this class doesn't prevent the main
046 * class loader from getting garbage collected, and this class can detect when
047 * the main class loader has been garbage collected and stop itself.
048 *
049 * @deprecated FinalizableReferenceQueue is an unsound mechanism for cleaning up references,
050 * because (1) it's single thread can be easily overloaded, and (2) it's insistance on running
051 * a background thread is problematic in certain environments. <b>This class is scheduled for
052 * deletion in December 2012.</b>
053 */
054 @Deprecated
055 public
056 class Finalizer extends Thread {
057
058 private static final Logger logger
059 = Logger.getLogger(Finalizer.class.getName());
060
061 /** Name of FinalizableReference.class. */
062 private static final String FINALIZABLE_REFERENCE
063 = "com.google.common.base.FinalizableReference";
064
065 /**
066 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
067 * reflectively.
068 *
069 * @param finalizableReferenceClass FinalizableReference.class
070 * @param frq reference to instance of FinalizableReferenceQueue that started
071 * this thread
072 * @return ReferenceQueue which Finalizer will poll
073 */
074 public static ReferenceQueue<Object> startFinalizer(
075 Class<?> finalizableReferenceClass, Object frq) {
076 /*
077 * We use FinalizableReference.class for two things:
078 *
079 * 1) To invoke FinalizableReference.finalizeReferent()
080 *
081 * 2) To detect when FinalizableReference's class loader has to be garbage
082 * collected, at which point, Finalizer can stop running
083 */
084 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
085 throw new IllegalArgumentException(
086 "Expected " + FINALIZABLE_REFERENCE + ".");
087 }
088
089 Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
090 finalizer.start();
091 return finalizer.queue;
092 }
093
094 private final WeakReference<Class<?>> finalizableReferenceClassReference;
095 private final PhantomReference<Object> frqReference;
096 private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
097
098 private static final Field inheritableThreadLocals
099 = getInheritableThreadLocalsField();
100
101 /** Constructs a new finalizer thread. */
102 private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
103 super(Finalizer.class.getName());
104
105 this.finalizableReferenceClassReference
106 = new WeakReference<Class<?>>(finalizableReferenceClass);
107
108 // Keep track of the FRQ that started us so we know when to stop.
109 this.frqReference = new PhantomReference<Object>(frq, queue);
110
111 setDaemon(true);
112
113 try {
114 if (inheritableThreadLocals != null) {
115 inheritableThreadLocals.set(this, null);
116 }
117 } catch (Throwable t) {
118 logger.log(Level.INFO, "Failed to clear thread local values inherited"
119 + " by reference finalizer thread.", t);
120 }
121
122 // TODO(fry): Priority?
123 }
124
125 /**
126 * Loops continuously, pulling references off the queue and cleaning them up.
127 */
128 @SuppressWarnings("InfiniteLoopStatement")
129 @Override
130 public void run() {
131 try {
132 while (true) {
133 try {
134 cleanUp(queue.remove());
135 } catch (InterruptedException e) { /* ignore */ }
136 }
137 } catch (ShutDown shutDown) { /* ignore */ }
138 }
139
140 /**
141 * Cleans up a single reference. Catches and logs all throwables.
142 */
143 private void cleanUp(Reference<?> reference) throws ShutDown {
144 Method finalizeReferentMethod = getFinalizeReferentMethod();
145 do {
146 /*
147 * This is for the benefit of phantom references. Weak and soft
148 * references will have already been cleared by this point.
149 */
150 reference.clear();
151
152 if (reference == frqReference) {
153 /*
154 * The client no longer has a reference to the
155 * FinalizableReferenceQueue. We can stop.
156 */
157 throw new ShutDown();
158 }
159
160 try {
161 finalizeReferentMethod.invoke(reference);
162 } catch (Throwable t) {
163 logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
164 }
165
166 /*
167 * Loop as long as we have references available so as not to waste
168 * CPU looking up the Method over and over again.
169 */
170 } while ((reference = queue.poll()) != null);
171 }
172
173 /**
174 * Looks up FinalizableReference.finalizeReferent() method.
175 */
176 private Method getFinalizeReferentMethod() throws ShutDown {
177 Class<?> finalizableReferenceClass
178 = finalizableReferenceClassReference.get();
179 if (finalizableReferenceClass == null) {
180 /*
181 * FinalizableReference's class loader was reclaimed. While there's a
182 * chance that other finalizable references could be enqueued
183 * subsequently (at which point the class loader would be resurrected
184 * by virtue of us having a strong reference to it), we should pretty
185 * much just shut down and make sure we don't keep it alive any longer
186 * than necessary.
187 */
188 throw new ShutDown();
189 }
190 try {
191 return finalizableReferenceClass.getMethod("finalizeReferent");
192 } catch (NoSuchMethodException e) {
193 throw new AssertionError(e);
194 }
195 }
196
197 public static Field getInheritableThreadLocalsField() {
198 try {
199 Field inheritableThreadLocals
200 = Thread.class.getDeclaredField("inheritableThreadLocals");
201 inheritableThreadLocals.setAccessible(true);
202 return inheritableThreadLocals;
203 } catch (Throwable t) {
204 logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
205 + " Reference finalizer threads will inherit thread local"
206 + " values.");
207 return null;
208 }
209 }
210
211 /** Indicates that it's time to shut down the Finalizer. */
212 @SuppressWarnings("serial") // Never serialized or thrown out of this class.
213 private static class ShutDown extends Exception { }
214 }