    001    /*
    002     * Copyright (C) 2008 The Guava Authors
    003     *
    004     * Licensed under the Apache License, Version 2.0 (the "License");
    005     * you may not use this file except in compliance with the License.
    006     * You may obtain a copy of the License at
    007     *
    008     *      http://www.apache.org/licenses/LICENSE-2.0
    009     *
    010     * Unless required by applicable law or agreed to in writing, software
    011     * distributed under the License is distributed on an "AS IS" BASIS,
    012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013     * See the License for the specific language governing permissions and
    014     * limitations under the License.
    015     */
    016
    017    package com.google.common.base.internal;
    018
    019    import java.lang.ref.PhantomReference;
    020    import java.lang.ref.Reference;
    021    import java.lang.ref.ReferenceQueue;
    022    import java.lang.ref.WeakReference;
    023    import java.lang.reflect.Field;
    024    import java.lang.reflect.Method;
    025    import java.util.logging.Level;
    026    import java.util.logging.Logger;
    027
    028    /**
    029     * Thread that finalizes referents. All references should implement
    030     * {@code com.google.common.base.FinalizableReference}.
    031     *
    032     * <p>While this class is public, we consider it to be *internal* and not part
    033     * of our published API. It is public so we can access it reflectively across
    034     * class loaders in secure environments.
    035     *
    036     * <p>This class can't depend on other Google Collections code. If we were
    037     * to load this class in the same class loader as the rest of
    038     * Google Collections, this thread would keep an indirect strong reference
    039     * to the class loader and prevent it from being garbage collected. This
    040     * poses a problem for environments where you want to throw away the class
    041     * loader. For example, dynamically reloading a web application or unloading
    042     * an OSGi bundle.
    043     *
    044     * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
    045     * in its own class loader. That way, this class doesn't prevent the main
    046     * class loader from getting garbage collected, and this class can detect when
    047     * the main class loader has been garbage collected and stop itself.
    048     */
    049    public class Finalizer extends Thread {
    050
    051      private static final Logger logger
    052          = Logger.getLogger(Finalizer.class.getName());
    053
    054      /** Name of FinalizableReference.class. */
    055      private static final String FINALIZABLE_REFERENCE
    056          = "com.google.common.base.FinalizableReference";
    057
    058      /**
    059       * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
    060       * reflectively.
    061       *
    062       * @param finalizableReferenceClass FinalizableReference.class
    063       * @param frq reference to instance of FinalizableReferenceQueue that started
    064       *  this thread
    065       * @return ReferenceQueue which Finalizer will poll
    066       */
    067      public static ReferenceQueue<Object> startFinalizer(
    068          Class<?> finalizableReferenceClass, Object frq) {
    069        /*
    070         * We use FinalizableReference.class for two things:
    071         *
    072         * 1) To invoke FinalizableReference.finalizeReferent()
    073         *
    074         * 2) To detect when FinalizableReference's class loader has to be garbage
    075         * collected, at which point, Finalizer can stop running
    076         */
    077        if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
    078          throw new IllegalArgumentException(
    079              "Expected " + FINALIZABLE_REFERENCE + ".");
    080        }
    081
    082        Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
    083        finalizer.start();
    084        return finalizer.queue;
    085      }
    086
    087      private final WeakReference<Class<?>> finalizableReferenceClassReference;
    088      private final PhantomReference<Object> frqReference;
    089      private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    090
    091      private static final Field inheritableThreadLocals
    092          = getInheritableThreadLocalsField();
    093
    094      /** Constructs a new finalizer thread. */
    095      private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
    096        super(Finalizer.class.getName());
    097
    098        this.finalizableReferenceClassReference
    099            = 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
    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    }