View Javadoc
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     */
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. */
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
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    */
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    */
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 
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 }