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        public Class<?> loadFinalizer() {
190          if (disabled) {
191            return null;
192          }
193          ClassLoader systemLoader;
194          try {
195            systemLoader = ClassLoader.getSystemClassLoader();
196          } catch (SecurityException e) {
197            logger.info("Not allowed to access system class loader.");
198            return null;
199          }
200          if (systemLoader != null) {
201            try {
202              return systemLoader.loadClass(FINALIZER_CLASS_NAME);
203            } catch (ClassNotFoundException e) {
204              // Ignore. Finalizer is simply in a child class loader.
205              return null;
206            }
207          } else {
208            return null;
209          }
210        }
211      }
212    
213      /**
214       * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
215       * our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
216       * it would prevent our class loader from getting garbage collected.
217       */
218      static class DecoupledLoader implements FinalizerLoader {
219        private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader."
220            + "Loading Finalizer in the current class loader instead. As a result, you will not be able"
221            + "to garbage collect this class loader. To support reclaiming this class loader, either"
222            + "resolve the underlying issue, or move Google Collections to your system class path.";
223    
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          // We use the bootstrap class loader as the parent because Finalizer by design uses
266          // only standard Java classes. That also means that FinalizableReferenceQueueTest
267          // doesn't pick up the wrong version of the Finalizer class.
268          return new URLClassLoader(new URL[] {base}, null);
269        }
270      }
271    
272      /**
273       * Loads Finalizer directly using the current class loader. We won't be able to garbage collect
274       * this class loader, but at least the world doesn't end.
275       */
276      static class DirectLoader implements FinalizerLoader {
277        public Class<?> loadFinalizer() {
278          try {
279            return Class.forName(FINALIZER_CLASS_NAME);
280          } catch (ClassNotFoundException e) {
281            throw new AssertionError(e);
282          }
283        }
284      }
285    
286      /**
287       * Looks up Finalizer.startFinalizer().
288       */
289      static Method getStartFinalizer(Class<?> finalizer) {
290        try {
291          return finalizer.getMethod("startFinalizer", Class.class, Object.class);
292        } catch (NoSuchMethodException e) {
293          throw new AssertionError(e);
294        }
295      }
296    }