• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.base.internal;
18 
19 import java.lang.ref.PhantomReference;
20 import java.lang.ref.Reference;
21 import java.lang.ref.ReferenceQueue;
22 import java.lang.ref.WeakReference;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27 
28 /**
29  * Thread that finalizes referents. All references should implement
30  * {@code com.google.common.base.FinalizableReference}.
31  *
32  * <p>While this class is public, we consider it to be *internal* and not part
33  * of our published API. It is public so we can access it reflectively across
34  * class loaders in secure environments.
35  *
36  * <p>This class can't depend on other Google Collections code. If we were
37  * to load this class in the same class loader as the rest of
38  * Google Collections, this thread would keep an indirect strong reference
39  * to the class loader and prevent it from being garbage collected. This
40  * poses a problem for environments where you want to throw away the class
41  * loader. For example, dynamically reloading a web application or unloading
42  * an OSGi bundle.
43  *
44  * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
45  * in its own class loader. That way, this class doesn't prevent the main
46  * class loader from getting garbage collected, and this class can detect when
47  * the main class loader has been garbage collected and stop itself.
48  */
49 public class Finalizer extends Thread {
50 
51   private static final Logger logger
52       = Logger.getLogger(Finalizer.class.getName());
53 
54   /** Name of FinalizableReference.class. */
55   private static final String FINALIZABLE_REFERENCE
56       = "com.google.common.base.FinalizableReference";
57 
58   /**
59    * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
60    * reflectively.
61    *
62    * @param finalizableReferenceClass FinalizableReference.class
63    * @param frq reference to instance of FinalizableReferenceQueue that started
64    *  this thread
65    * @return ReferenceQueue which Finalizer will poll
66    */
startFinalizer( Class<?> finalizableReferenceClass, Object frq)67   public static ReferenceQueue<Object> startFinalizer(
68       Class<?> finalizableReferenceClass, Object frq) {
69     /*
70      * We use FinalizableReference.class for two things:
71      *
72      * 1) To invoke FinalizableReference.finalizeReferent()
73      *
74      * 2) To detect when FinalizableReference's class loader has to be garbage
75      * collected, at which point, Finalizer can stop running
76      */
77     if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
78       throw new IllegalArgumentException(
79           "Expected " + FINALIZABLE_REFERENCE + ".");
80     }
81 
82     Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
83     finalizer.start();
84     return finalizer.queue;
85   }
86 
87   private final WeakReference<Class<?>> finalizableReferenceClassReference;
88   private final PhantomReference<Object> frqReference;
89   private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
90 
91   private static final Field inheritableThreadLocals
92       = getInheritableThreadLocalsField();
93 
94   /** Constructs a new finalizer thread. */
Finalizer(Class<?> finalizableReferenceClass, Object frq)95   private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
96     super(Finalizer.class.getName());
97 
98     this.finalizableReferenceClassReference
99         = new WeakReference<Class<?>>(finalizableReferenceClass);
100 
101     // Keep track of the FRQ that started us so we know when to stop.
102     this.frqReference = new PhantomReference<Object>(frq, queue);
103 
104     setDaemon(true);
105 
106     try {
107       if (inheritableThreadLocals != null) {
108         inheritableThreadLocals.set(this, null);
109       }
110     } catch (Throwable t) {
111       logger.log(Level.INFO, "Failed to clear thread local values inherited"
112           + " by reference finalizer thread.", t);
113     }
114 
115     // TODO(fry): Priority?
116   }
117 
118   /**
119    * Loops continuously, pulling references off the queue and cleaning them up.
120    */
121   @SuppressWarnings("InfiniteLoopStatement")
122   @Override
run()123   public void run() {
124     try {
125       while (true) {
126         try {
127           cleanUp(queue.remove());
128         } catch (InterruptedException e) { /* ignore */ }
129       }
130     } catch (ShutDown shutDown) { /* ignore */ }
131   }
132 
133   /**
134    * Cleans up a single reference. Catches and logs all throwables.
135    */
cleanUp(Reference<?> reference)136   private void cleanUp(Reference<?> reference) throws ShutDown {
137     Method finalizeReferentMethod = getFinalizeReferentMethod();
138     do {
139       /*
140        * This is for the benefit of phantom references. Weak and soft
141        * references will have already been cleared by this point.
142        */
143       reference.clear();
144 
145       if (reference == frqReference) {
146         /*
147          * The client no longer has a reference to the
148          * FinalizableReferenceQueue. We can stop.
149          */
150         throw new ShutDown();
151       }
152 
153       try {
154         finalizeReferentMethod.invoke(reference);
155       } catch (Throwable t) {
156         logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
157       }
158 
159       /*
160        * Loop as long as we have references available so as not to waste
161        * CPU looking up the Method over and over again.
162        */
163     } while ((reference = queue.poll()) != null);
164   }
165 
166   /**
167    * Looks up FinalizableReference.finalizeReferent() method.
168    */
getFinalizeReferentMethod()169   private Method getFinalizeReferentMethod() throws ShutDown {
170     Class<?> finalizableReferenceClass
171         = finalizableReferenceClassReference.get();
172     if (finalizableReferenceClass == null) {
173       /*
174        * FinalizableReference's class loader was reclaimed. While there's a
175        * chance that other finalizable references could be enqueued
176        * subsequently (at which point the class loader would be resurrected
177        * by virtue of us having a strong reference to it), we should pretty
178        * much just shut down and make sure we don't keep it alive any longer
179        * than necessary.
180        */
181       throw new ShutDown();
182     }
183     try {
184       return finalizableReferenceClass.getMethod("finalizeReferent");
185     } catch (NoSuchMethodException e) {
186       throw new AssertionError(e);
187     }
188   }
189 
getInheritableThreadLocalsField()190   public static Field getInheritableThreadLocalsField() {
191     try {
192       Field inheritableThreadLocals
193           = Thread.class.getDeclaredField("inheritableThreadLocals");
194       inheritableThreadLocals.setAccessible(true);
195       return inheritableThreadLocals;
196     } catch (Throwable t) {
197       logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
198           + " Reference finalizer threads will inherit thread local"
199           + " values.");
200       return null;
201     }
202   }
203 
204   /** Indicates that it's time to shut down the Finalizer. */
205   @SuppressWarnings("serial") // Never serialized or thrown out of this class.
206   private static class ShutDown extends Exception { }
207 }
208