Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2008 Google Inc.
      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    */
     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. */
     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: Priority?
    116   }
    117 
    118   /**
    119    * Loops continuously, pulling references off the queue and cleaning them up.
    120    */
    121   @SuppressWarnings("InfiniteLoopStatement")
    122   @Override
    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    */
    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    */
    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 
    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