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