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