    001    /*
    002     * Copyright (C) 2007 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;
    018
    019    import com.google.common.annotations.VisibleForTesting;
    020
    021    import java.io.FileNotFoundException;
    022    import java.io.IOException;
    023    import java.lang.ref.Reference;
    024    import java.lang.ref.ReferenceQueue;
    025    import java.lang.reflect.Method;
    026    import java.net.URL;
    027    import java.net.URLClassLoader;
    028    import java.util.logging.Level;
    029    import java.util.logging.Logger;
    030
    031    /**
    032     * A reference queue with an associated background thread that dequeues references and invokes
    033     * {@link FinalizableReference#finalizeReferent()} on them.
    034     *
    035     * <p>Keep a strong reference to this object until all of the associated referents have been
    036     * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code
    037     * finalizeReferent()} on the remaining references.
    038     *
    039     * @author Bob Lee
    040     * @since 2.0 (imported from Google Collections Library)
    041     */
    042    public class FinalizableReferenceQueue {
    043      /*
    044       * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
    045       * map built by MapMaker) no longer has a strong reference to this object, the garbage collector
    046       * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
    047       * Finalizer to stop.
    048       *
    049       * If this library is loaded in the system class loader, FinalizableReferenceQueue can load
    050       * Finalizer directly with no problems.
    051       *
    052       * If this library is loaded in an application class loader, it's important that Finalizer not
    053       * have a strong reference back to the class loader. Otherwise, you could have a graph like this:
    054       *
    055       * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader
    056       * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance
    057       *
    058       * Even if no other references to classes from the application class loader remain, the Finalizer
    059       * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the
    060       * Finalizer running, and as a result, the application class loader can never be reclaimed.
    061       *
    062       * This means that dynamically loaded web applications and OSGi bundles can't be unloaded.
    063       *
    064       * If the library is loaded in an application class loader, we try to break the cycle by loading
    065       * Finalizer in its own independent class loader:
    066       *
    067       * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue
    068       * -> etc. -> Decoupled class loader -> Finalizer
    069       *
    070       * Now, Finalizer no longer keeps an indirect strong reference to the static
    071       * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed
    072       * at which point the Finalizer thread will stop and its decoupled class loader can also be
    073       * reclaimed.
    074       *
    075       * If any of this fails along the way, we fall back to loading Finalizer directly in the
    076       * application class loader.
    077       */
    078
    079      private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
    080
    081      private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
    082
    083      /** Reference to Finalizer.startFinalizer(). */
    084      private static final Method startFinalizer;
    085      static {
    086        Class<?> finalizer = loadFinalizer(
    087            new SystemLoader(), new DecoupledLoader(), new DirectLoader());
    088        startFinalizer = getStartFinalizer(finalizer);
    089      }
    090
    091      /**
    092       * The actual reference queue that our background thread will poll.
    093       */
    094      final ReferenceQueue<Object> queue;
    095
    096      /**
    097       * Whether or not the background thread started successfully.
    098       */
    099      final boolean threadStarted;
    100
    101      /**
    102       * Constructs a new queue.
    103       */
    104      @SuppressWarnings("unchecked")
    105      public FinalizableReferenceQueue() {
    106        // We could start the finalizer lazily, but I'd rather it blow up early.
    107        ReferenceQueue<Object> queue;
    108        boolean threadStarted = false;
    109        try {
    110          queue = (ReferenceQueue<Object>)
    111              startFinalizer.invoke(null, FinalizableReference.class, this);
    112          threadStarted = true;
    113        } catch (IllegalAccessException impossible) {
    114          throw new AssertionError(impossible); // startFinalizer() is public
    115        } catch (Throwable t) {
    116          logger.log(Level.INFO, "Failed to start reference finalizer thread."
    117              + " Reference cleanup will only occur when new references are created.", t);
    118          queue = new ReferenceQueue<Object>();
    119        }
    120
    121        this.queue = queue;
    122        this.threadStarted = threadStarted;
    123      }
    124
    125      /**
    126       * Repeatedly dequeues references from the queue and invokes {@link
    127       * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a
    128       * no-op if the background thread was created successfully.
    129       */
    130      void cleanUp() {
    131        if (threadStarted) {
    132          return;
    133        }
    134
    135        Reference<?> reference;
    136        while ((reference = queue.poll()) != null) {
    137          /*
    138           * This is for the benefit of phantom references. Weak and soft references will have already
    139           * been cleared by this point.
    140           */
    141          reference.clear();
    142          try {
    143            ((FinalizableReference) reference).finalizeReferent();
    144          } catch (Throwable t) {
    145            logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
    146          }
    147        }
    148      }
    149
    150      /**
    151       * Iterates through the given loaders until it finds one that can load Finalizer.
    152       *
    153       * @return Finalizer.class
    154       */
    155      private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
    156        for (FinalizerLoader loader : loaders) {
    157          Class<?> finalizer = loader.loadFinalizer();
    158          if (finalizer != null) {
    159            return finalizer;
    160          }
    161        }
    162
    163        throw new AssertionError();
    164      }
    165
    166      /**
    167       * Loads Finalizer.class.
    168       */
    169      interface FinalizerLoader {
    170
    171        /**
    172         * Returns Finalizer.class or null if this loader shouldn't or can't load it.
    173         *
    174         * @throws SecurityException if we don't have the appropriate privileges
    175         */
    176        Class<?> loadFinalizer();
    177      }
    178
    179      /**
    180       * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path,
    181       * we needn't create a separate loader.
    182       */
    183      static class SystemLoader implements FinalizerLoader {
    184        // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable
    185        // finding Finalizer on the system class path even if it is there.
    186        @VisibleForTesting
    187        static boolean disabled;
    188
    189        @Override
    190        public Class<?> loadFinalizer() {
    191          if (disabled) {
    192            return null;
    193          }
    194          ClassLoader systemLoader;
    195          try {
    196            systemLoader = ClassLoader.getSystemClassLoader();
    197          } catch (SecurityException e) {
    198            logger.info("Not allowed to access system class loader.");
    199            return null;
    200          }
    201          if (systemLoader != null) {
    202            try {
    203              return systemLoader.loadClass(FINALIZER_CLASS_NAME);
    204            } catch (ClassNotFoundException e) {
    205              // Ignore. Finalizer is simply in a child class loader.
    206              return null;
    207            }
    208          } else {
    209            return null;
    210          }
    211        }
    212      }
    213
    214      /**
    215       * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
    216       * our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
    217       * it would prevent our class loader from getting garbage collected.
    218       */
    219      static class DecoupledLoader implements FinalizerLoader {
    220        private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader."
    221            + "Loading Finalizer in the current class loader instead. As a result, you will not be able"
    222            + "to garbage collect this class loader. To support reclaiming this class loader, either"
    223            + "resolve the underlying issue, or move Google Collections to your system class path.";
    224
    225        @Override
    226        public Class<?> loadFinalizer() {
    227          try {
    228            /*
    229             * We use URLClassLoader because it's the only concrete class loader implementation in the
    230             * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this
    231             * class loader:
    232             *
    233             * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader
    234             *
    235             * System class loader will (and must) be the parent.
    236             */
    237            ClassLoader finalizerLoader = newLoader(getBaseUrl());
    238            return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
    239          } catch (Exception e) {
    240            logger.log(Level.WARNING, LOADING_ERROR, e);
    241            return null;
    242          }
    243        }
    244
    245        /**
    246         * Gets URL for base of path containing Finalizer.class.
    247         */
    248        URL getBaseUrl() throws IOException {
    249          // Find URL pointing to Finalizer.class file.
    250          String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
    251          URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
    252          if (finalizerUrl == null) {
    253            throw new FileNotFoundException(finalizerPath);
    254          }
    255
    256          // Find URL pointing to base of class path.
    257          String urlString = finalizerUrl.toString();
    258          if (!urlString.endsWith(finalizerPath)) {
    259            throw new IOException("Unsupported path style: " + urlString);
    260          }
    261          urlString = urlString.substring(0, urlString.length() - finalizerPath.length());
    262          return new URL(finalizerUrl, urlString);
    263        }
    264
    265        /** Creates a class loader with the given base URL as its classpath. */
    266        URLClassLoader newLoader(URL base) {
    267          // We use the bootstrap class loader as the parent because Finalizer by design uses
    268          // only standard Java classes. That also means that FinalizableReferenceQueueTest
    269          // doesn't pick up the wrong version of the Finalizer class.
    270          return new URLClassLoader(new URL[] {base}, null);
    271        }
    272      }
    273
    274      /**
    275       * Loads Finalizer directly using the current class loader. We won't be able to garbage collect
    276       * this class loader, but at least the world doesn't end.
    277       */
    278      static class DirectLoader implements FinalizerLoader {
    279        @Override
    280        public Class<?> loadFinalizer() {
    281          try {
    282            return Class.forName(FINALIZER_CLASS_NAME);
    283          } catch (ClassNotFoundException e) {
    284            throw new AssertionError(e);
    285          }
    286        }
    287      }
    288
    289      /**
    290       * Looks up Finalizer.startFinalizer().
    291       */
    292      static Method getStartFinalizer(Class<?> finalizer) {
    293        try {
    294          return finalizer.getMethod("startFinalizer", Class.class, Object.class);
    295        } catch (NoSuchMethodException e) {
    296          throw new AssertionError(e);
    297        }
    298      }
    299    }