Home | History | Annotate | Download | only in internal
      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 }
    215