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 implements Runnable {
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 queue a reference queue that the thread will poll.
64    * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be
65    * queued either when the FinalizableReferenceQueue is no longer referenced anywhere, or when
66    * its close() method is called.
67    */
startFinalizer( Class<?> finalizableReferenceClass, ReferenceQueue<Object> queue, PhantomReference<Object> frqReference)68   public static void startFinalizer(
69       Class<?> finalizableReferenceClass,
70       ReferenceQueue<Object> queue,
71       PhantomReference<Object> frqReference) {
72     /*
73      * We use FinalizableReference.class for two things:
74      *
75      * 1) To invoke FinalizableReference.finalizeReferent()
76      *
77      * 2) To detect when FinalizableReference's class loader has to be garbage
78      * collected, at which point, Finalizer can stop running
79      */
80     if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
81       throw new IllegalArgumentException(
82           "Expected " + FINALIZABLE_REFERENCE + ".");
83     }
84 
85     Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference);
86     Thread thread = new Thread(finalizer);
87     thread.setName(Finalizer.class.getName());
88     thread.setDaemon(true);
89 
90     try {
91       if (inheritableThreadLocals != null) {
92         inheritableThreadLocals.set(thread, null);
93       }
94     } catch (Throwable t) {
95       logger.log(Level.INFO, "Failed to clear thread local values inherited"
96           + " by reference finalizer thread.", t);
97     }
98 
99     thread.start();
100   }
101 
102   private final WeakReference<Class<?>> finalizableReferenceClassReference;
103   private final PhantomReference<Object> frqReference;
104   private final ReferenceQueue<Object> queue;
105 
106   private static final Field inheritableThreadLocals
107       = getInheritableThreadLocalsField();
108 
109   /** Constructs a new finalizer thread. */
Finalizer( Class<?> finalizableReferenceClass, ReferenceQueue<Object> queue, PhantomReference<Object> frqReference)110   private Finalizer(
111       Class<?> finalizableReferenceClass,
112       ReferenceQueue<Object> queue,
113       PhantomReference<Object> frqReference) {
114     this.queue = queue;
115 
116     this.finalizableReferenceClassReference
117         = new WeakReference<Class<?>>(finalizableReferenceClass);
118 
119     // Keep track of the FRQ that started us so we know when to stop.
120     this.frqReference = frqReference;
121   }
122 
123   /**
124    * Loops continuously, pulling references off the queue and cleaning them up.
125    */
126   @SuppressWarnings("InfiniteLoopStatement")
127   @Override
run()128   public void run() {
129     while (true) {
130       try {
131         if (!cleanUp(queue.remove())) {
132           break;
133         }
134       } catch (InterruptedException e) { /* ignore */ }
135     }
136   }
137 
138   /**
139    * Cleans up a single reference. Catches and logs all throwables.
140    * @return true if the caller should continue, false if the associated FinalizableReferenceQueue
141    * is no longer referenced.
142    */
cleanUp(Reference<?> reference)143   private boolean cleanUp(Reference<?> reference) {
144     Method finalizeReferentMethod = getFinalizeReferentMethod();
145     if (finalizeReferentMethod == null) {
146       return false;
147     }
148     do {
149       /*
150        * This is for the benefit of phantom references. Weak and soft
151        * references will have already been cleared by this point.
152        */
153       reference.clear();
154 
155       if (reference == frqReference) {
156         /*
157          * The client no longer has a reference to the
158          * FinalizableReferenceQueue. We can stop.
159          */
160         return false;
161       }
162 
163       try {
164         finalizeReferentMethod.invoke(reference);
165       } catch (Throwable t) {
166         logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
167       }
168 
169       /*
170        * Loop as long as we have references available so as not to waste
171        * CPU looking up the Method over and over again.
172        */
173     } while ((reference = queue.poll()) != null);
174     return true;
175   }
176 
177   /**
178    * Looks up FinalizableReference.finalizeReferent() method.
179    */
getFinalizeReferentMethod()180   private Method getFinalizeReferentMethod() {
181     Class<?> finalizableReferenceClass
182         = finalizableReferenceClassReference.get();
183     if (finalizableReferenceClass == null) {
184       /*
185        * FinalizableReference's class loader was reclaimed. While there's a
186        * chance that other finalizable references could be enqueued
187        * subsequently (at which point the class loader would be resurrected
188        * by virtue of us having a strong reference to it), we should pretty
189        * much just shut down and make sure we don't keep it alive any longer
190        * than necessary.
191        */
192       return null;
193     }
194     try {
195       return finalizableReferenceClass.getMethod("finalizeReferent");
196     } catch (NoSuchMethodException e) {
197       throw new AssertionError(e);
198     }
199   }
200 
getInheritableThreadLocalsField()201   public static Field getInheritableThreadLocalsField() {
202     try {
203       Field inheritableThreadLocals
204           = Thread.class.getDeclaredField("inheritableThreadLocals");
205       inheritableThreadLocals.setAccessible(true);
206       return inheritableThreadLocals;
207     } catch (Throwable t) {
208       logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
209           + " Reference finalizer threads will inherit thread local"
210           + " values.");
211       return null;
212     }
213   }
214 }
215