Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import java.io.FileNotFoundException;
     20 import java.io.IOException;
     21 import java.lang.ref.Reference;
     22 import java.lang.ref.ReferenceQueue;
     23 import java.lang.reflect.Method;
     24 import java.net.URL;
     25 import java.net.URLClassLoader;
     26 import java.util.logging.Level;
     27 import java.util.logging.Logger;
     28 
     29 /**
     30  * A reference queue with an associated background thread that dequeues
     31  * references and invokes {@link FinalizableReference#finalizeReferent()} on
     32  * them.
     33  *
     34  * <p>Keep a strong reference to this object until all of the associated
     35  * referents have been finalized. If this object is garbage collected earlier,
     36  * the backing thread will not invoke {@code finalizeReferent()} on the
     37  * remaining references.
     38  *
     39  * @author Bob Lee
     40  * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library)
     41  */
     42 public class FinalizableReferenceQueue {
     43 
     44   /*
     45    * The Finalizer thread keeps a phantom reference to this object. When the
     46    * client (ReferenceMap, for example) no longer has a strong reference to
     47    * this object, the garbage collector will reclaim it and enqueue the
     48    * phantom reference. The enqueued reference will trigger the Finalizer to
     49    * stop.
     50    *
     51    * If this library is loaded in the system class loader,
     52    * FinalizableReferenceQueue can load Finalizer directly with no problems.
     53    *
     54    * If this library is loaded in an application class loader, it's important
     55    * that Finalizer not have a strong reference back to the class loader.
     56    * Otherwise, you could have a graph like this:
     57    *
     58    * Finalizer Thread
     59    *   runs instance of -> Finalizer.class
     60    *     loaded by -> Application class loader
     61    *       which loaded -> ReferenceMap.class
     62    *         which has a static -> FinalizableReferenceQueue instance
     63    *
     64    * Even if no other references to classes from the application class loader
     65    * remain, the Finalizer thread keeps an indirect strong reference to the
     66    * queue in ReferenceMap, which keeps the Finalizer running, and as a result,
     67    * the application class loader can never be reclaimed.
     68    *
     69    * This means that dynamically loaded web applications and OSGi bundles can't
     70    * be unloaded.
     71    *
     72    * If the library is loaded in an application class loader, we try to break
     73    * the cycle by loading Finalizer in its own independent class loader:
     74    *
     75    * System class loader
     76    *   -> Application class loader
     77    *     -> ReferenceMap
     78    *     -> FinalizableReferenceQueue
     79    *     -> etc.
     80    *   -> Decoupled class loader
     81    *     -> Finalizer
     82    *
     83    * Now, Finalizer no longer keeps an indirect strong reference to the
     84    * static FinalizableReferenceQueue field in ReferenceMap. The application
     85    * class loader can be reclaimed at which point the Finalizer thread will
     86    * stop and its decoupled class loader can also be reclaimed.
     87    *
     88    * If any of this fails along the way, we fall back to loading Finalizer
     89    * directly in the application class loader.
     90    */
     91 
     92   private static final Logger logger
     93       = Logger.getLogger(FinalizableReferenceQueue.class.getName());
     94 
     95   private static final String FINALIZER_CLASS_NAME
     96       = "com.google.common.base.internal.Finalizer";
     97 
     98   /** Reference to Finalizer.startFinalizer(). */
     99   private static final Method startFinalizer;
    100   static {
    101     Class<?> finalizer = loadFinalizer(
    102         new SystemLoader(), new DecoupledLoader(), new DirectLoader());
    103     startFinalizer = getStartFinalizer(finalizer);
    104   }
    105 
    106   /**
    107    * The actual reference queue that our background thread will poll.
    108    */
    109   final ReferenceQueue<Object> queue;
    110 
    111   /**
    112    * Whether or not the background thread started successfully.
    113    */
    114   final boolean threadStarted;
    115 
    116   /**
    117    * Constructs a new queue.
    118    */
    119   @SuppressWarnings("unchecked")
    120   public FinalizableReferenceQueue() {
    121     // We could start the finalizer lazily, but I'd rather it blow up early.
    122     ReferenceQueue<Object> queue;
    123     boolean threadStarted = false;
    124     try {
    125       queue = (ReferenceQueue<Object>) startFinalizer.invoke(null,
    126           FinalizableReference.class, this);
    127       threadStarted = true;
    128     } catch (IllegalAccessException e) {
    129       // Finalizer.startFinalizer() is public.
    130       throw new AssertionError(e);
    131     } catch (Throwable t) {
    132       logger.log(Level.INFO, "Failed to start reference finalizer thread."
    133           + " Reference cleanup will only occur when new references are"
    134           + " created.", t);
    135       queue = new ReferenceQueue<Object>();
    136     }
    137 
    138     this.queue = queue;
    139     this.threadStarted = threadStarted;
    140   }
    141 
    142   /**
    143    * Repeatedly dequeues references from the queue and invokes
    144    * {@link FinalizableReference#finalizeReferent()} on them until the queue
    145    * is empty. This method is a no-op if the background thread was created
    146    * successfully.
    147    */
    148   void cleanUp() {
    149     if (threadStarted) {
    150       return;
    151     }
    152 
    153     Reference<?> reference;
    154     while ((reference = queue.poll()) != null) {
    155       /*
    156        * This is for the benefit of phantom references. Weak and soft
    157        * references will have already been cleared by this point.
    158        */
    159       reference.clear();
    160       try {
    161         ((FinalizableReference) reference).finalizeReferent();
    162       } catch (Throwable t) {
    163         logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
    164       }
    165     }
    166   }
    167 
    168   /**
    169    * Iterates through the given loaders until it finds one that can load
    170    * Finalizer.
    171    *
    172    * @return Finalizer.class
    173    */
    174   private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
    175     for (FinalizerLoader loader : loaders) {
    176       Class<?> finalizer = loader.loadFinalizer();
    177       if (finalizer != null) {
    178         return finalizer;
    179       }
    180     }
    181 
    182     throw new AssertionError();
    183   }
    184 
    185   /**
    186    * Loads Finalizer.class.
    187    */
    188   interface FinalizerLoader {
    189 
    190     /**
    191      * Returns Finalizer.class or null if this loader shouldn't or can't load
    192      * it.
    193      *
    194      * @throws SecurityException if we don't have the appropriate privileges
    195      */
    196     Class<?> loadFinalizer();
    197   }
    198 
    199   /**
    200    * Tries to load Finalizer from the system class loader. If Finalizer is
    201    * in the system class path, we needn't create a separate loader.
    202    */
    203   static class SystemLoader implements FinalizerLoader {
    204     public Class<?> loadFinalizer() {
    205       ClassLoader systemLoader;
    206       try {
    207         systemLoader = ClassLoader.getSystemClassLoader();
    208       } catch (SecurityException e) {
    209         logger.info("Not allowed to access system class loader.");
    210         return null;
    211       }
    212       if (systemLoader != null) {
    213         try {
    214           return systemLoader.loadClass(FINALIZER_CLASS_NAME);
    215         } catch (ClassNotFoundException e) {
    216           // Ignore. Finalizer is simply in a child class loader.
    217           return null;
    218         }
    219       } else {
    220         return null;
    221       }
    222     }
    223   }
    224 
    225   /**
    226    * Try to load Finalizer in its own class loader. If Finalizer's thread
    227    * had a direct reference to our class loader (which could be that of
    228    * a dynamically loaded web application or OSGi bundle), it would prevent
    229    * our class loader from getting garbage collected.
    230    */
    231   static class DecoupledLoader implements FinalizerLoader {
    232 
    233     private static final String LOADING_ERROR = "Could not load Finalizer in"
    234         + " its own class loader. Loading Finalizer in the current class loader"
    235         + " instead. As a result, you will not be able to garbage collect this"
    236         + " class loader. To support reclaiming this class loader, either"
    237         + " resolve the underlying issue, or move Google Collections to your"
    238         + " system class path.";
    239 
    240     public Class<?> loadFinalizer() {
    241       try {
    242         /*
    243          * We use URLClassLoader because it's the only concrete class loader
    244          * implementation in the JDK. If we used our own ClassLoader subclass,
    245          * Finalizer would indirectly reference this class loader:
    246          *
    247          * Finalizer.class ->
    248          *   CustomClassLoader ->
    249          *     CustomClassLoader.class ->
    250          *       This class loader
    251          *
    252          * System class loader will (and must) be the parent.
    253          */
    254         ClassLoader finalizerLoader = newLoader(getBaseUrl());
    255         return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
    256       } catch (Exception e) {
    257         logger.log(Level.WARNING, LOADING_ERROR, e);
    258         return null;
    259       }
    260     }
    261 
    262     /**
    263      * Gets URL for base of path containing Finalizer.class.
    264      */
    265     URL getBaseUrl() throws IOException {
    266       // Find URL pointing to Finalizer.class file.
    267       String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
    268       URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
    269       if (finalizerUrl == null) {
    270         throw new FileNotFoundException(finalizerPath);
    271       }
    272 
    273       // Find URL pointing to base of class path.
    274       String urlString = finalizerUrl.toString();
    275       if (!urlString.endsWith(finalizerPath)) {
    276         throw new IOException("Unsupported path style: " + urlString);
    277       }
    278       urlString = urlString.substring(0,
    279           urlString.length() - finalizerPath.length());
    280       return new URL(finalizerUrl, urlString);
    281     }
    282 
    283     /** Creates a class loader with the given base URL as its classpath. */
    284     URLClassLoader newLoader(URL base) {
    285       return new URLClassLoader(new URL[] { base });
    286     }
    287   }
    288 
    289   /**
    290    * Loads Finalizer directly using the current class loader. We won't be
    291    * able to garbage collect this class loader, but at least the world
    292    * doesn't end.
    293    */
    294   static class DirectLoader implements FinalizerLoader {
    295     public Class<?> loadFinalizer() {
    296       try {
    297         return Class.forName(FINALIZER_CLASS_NAME);
    298       } catch (ClassNotFoundException e) {
    299         throw new AssertionError(e);
    300       }
    301     }
    302   }
    303 
    304   /**
    305    * Looks up Finalizer.startFinalizer().
    306    */
    307   static Method getStartFinalizer(Class<?> finalizer) {
    308     try {
    309       return finalizer.getMethod("startFinalizer", Class.class, Object.class);
    310     } catch (NoSuchMethodException e) {
    311       throw new AssertionError(e);
    312     }
    313   }
    314 }
    315