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