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