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    }