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