Coverage Summary for Class: FinalizableReferenceQueue (com.google.common.base)

Class Method, % Line, %
FinalizableReferenceQueue 0% (0/7) 0% (0/34)
FinalizableReferenceQueue$DecoupledLoader 0% (0/4) 0% (0/16)
FinalizableReferenceQueue$DirectLoader 0% (0/2) 0% (0/4)
FinalizableReferenceQueue$SystemLoader 0% (0/2) 0% (0/13)
Total 0% (0/15) 0% (0/67)


1 /* 2  * Copyright (C) 2007 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.base; 16  17 import com.google.common.annotations.GwtIncompatible; 18 import com.google.common.annotations.VisibleForTesting; 19 import java.io.Closeable; 20 import java.io.FileNotFoundException; 21 import java.io.IOException; 22 import java.lang.ref.PhantomReference; 23 import java.lang.ref.Reference; 24 import java.lang.ref.ReferenceQueue; 25 import java.lang.reflect.Method; 26 import java.net.URL; 27 import java.net.URLClassLoader; 28 import java.util.logging.Level; 29 import java.util.logging.Logger; 30 import javax.annotation.CheckForNull; 31  32 /** 33  * A reference queue with an associated background thread that dequeues references and invokes 34  * {@link FinalizableReference#finalizeReferent()} on them. 35  * 36  * <p>Keep a strong reference to this object until all of the associated referents have been 37  * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code 38  * finalizeReferent()} on the remaining references. 39  * 40  * <p>As an example of how this is used, imagine you have a class {@code MyServer} that creates a 41  * {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the {@code 42  * ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without calling 43  * its {@code close} method. You <em>could</em> use a finalizer to accomplish this, but that has a 44  * number of well-known problems. Here is how you might use this class instead: 45  * 46  * <pre>{@code 47  * public class MyServer implements Closeable { 48  * private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue(); 49  * // You might also share this between several objects. 50  * 51  * private static final Set<Reference<?>> references = Sets.newConcurrentHashSet(); 52  * // This ensures that the FinalizablePhantomReference itself is not garbage-collected. 53  * 54  * private final ServerSocket serverSocket; 55  * 56  * private MyServer(...) { 57  * ... 58  * this.serverSocket = new ServerSocket(...); 59  * ... 60  * } 61  * 62  * public static MyServer create(...) { 63  * MyServer myServer = new MyServer(...); 64  * final ServerSocket serverSocket = myServer.serverSocket; 65  * Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) { 66  * public void finalizeReferent() { 67  * references.remove(this): 68  * if (!serverSocket.isClosed()) { 69  * ...log a message about how nobody called close()... 70  * try { 71  * serverSocket.close(); 72  * } catch (IOException e) { 73  * ... 74  * } 75  * } 76  * } 77  * }; 78  * references.add(reference); 79  * return myServer; 80  * } 81  * 82  * public void close() { 83  * serverSocket.close(); 84  * } 85  * } 86  * }</pre> 87  * 88  * @author Bob Lee 89  * @since 2.0 90  */ 91 @GwtIncompatible 92 @ElementTypesAreNonnullByDefault 93 public class FinalizableReferenceQueue implements Closeable { 94  /* 95  * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a 96  * map built by MapMaker) no longer has a strong reference to this object, the garbage collector 97  * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the 98  * Finalizer to stop. 99  * 100  * If this library is loaded in the system class loader, FinalizableReferenceQueue can load 101  * Finalizer directly with no problems. 102  * 103  * If this library is loaded in an application class loader, it's important that Finalizer not 104  * have a strong reference back to the class loader. Otherwise, you could have a graph like this: 105  * 106  * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader 107  * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance 108  * 109  * Even if no other references to classes from the application class loader remain, the Finalizer 110  * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the 111  * Finalizer running, and as a result, the application class loader can never be reclaimed. 112  * 113  * This means that dynamically loaded web applications and OSGi bundles can't be unloaded. 114  * 115  * If the library is loaded in an application class loader, we try to break the cycle by loading 116  * Finalizer in its own independent class loader: 117  * 118  * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue -> 119  * etc. -> Decoupled class loader -> Finalizer 120  * 121  * Now, Finalizer no longer keeps an indirect strong reference to the static 122  * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed 123  * at which point the Finalizer thread will stop and its decoupled class loader can also be 124  * reclaimed. 125  * 126  * If any of this fails along the way, we fall back to loading Finalizer directly in the 127  * application class loader. 128  * 129  * NOTE: The tests for this behavior (FinalizableReferenceQueueClassLoaderUnloadingTest) fail 130  * strangely when run in JDK 9. We are considering this a known issue. Please see 131  * https://github.com/google/guava/issues/3086 for more information. 132  */ 133  134  private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); 135  136  private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer"; 137  138  /** Reference to Finalizer.startFinalizer(). */ 139  private static final Method startFinalizer; 140  141  static { 142  Class<?> finalizer = 143  loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader()); 144  startFinalizer = getStartFinalizer(finalizer); 145  } 146  147  /** The actual reference queue that our background thread will poll. */ 148  final ReferenceQueue<Object> queue; 149  150  final PhantomReference<Object> frqRef; 151  152  /** Whether or not the background thread started successfully. */ 153  final boolean threadStarted; 154  155  /** Constructs a new queue. */ 156  public FinalizableReferenceQueue() { 157  // We could start the finalizer lazily, but I'd rather it blow up early. 158  queue = new ReferenceQueue<>(); 159  frqRef = new PhantomReference<Object>(this, queue); 160  boolean threadStarted = false; 161  try { 162  startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); 163  threadStarted = true; 164  } catch (IllegalAccessException impossible) { 165  throw new AssertionError(impossible); // startFinalizer() is public 166  } catch (Throwable t) { 167  logger.log( 168  Level.INFO, 169  "Failed to start reference finalizer thread." 170  + " Reference cleanup will only occur when new references are created.", 171  t); 172  } 173  174  this.threadStarted = threadStarted; 175  } 176  177  @Override 178  public void close() { 179  frqRef.enqueue(); 180  cleanUp(); 181  } 182  183  /** 184  * Repeatedly dequeues references from the queue and invokes {@link 185  * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a 186  * no-op if the background thread was created successfully. 187  */ 188  void cleanUp() { 189  if (threadStarted) { 190  return; 191  } 192  193  Reference<?> reference; 194  while ((reference = queue.poll()) != null) { 195  /* 196  * This is for the benefit of phantom references. Weak and soft references will have already 197  * been cleared by this point. 198  */ 199  reference.clear(); 200  try { 201  ((FinalizableReference) reference).finalizeReferent(); 202  } catch (Throwable t) { 203  logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 204  } 205  } 206  } 207  208  /** 209  * Iterates through the given loaders until it finds one that can load Finalizer. 210  * 211  * @return Finalizer.class 212  */ 213  private static Class<?> loadFinalizer(FinalizerLoader... loaders) { 214  for (FinalizerLoader loader : loaders) { 215  Class<?> finalizer = loader.loadFinalizer(); 216  if (finalizer != null) { 217  return finalizer; 218  } 219  } 220  221  throw new AssertionError(); 222  } 223  224  /** Loads Finalizer.class. */ 225  interface FinalizerLoader { 226  227  /** 228  * Returns Finalizer.class or null if this loader shouldn't or can't load it. 229  * 230  * @throws SecurityException if we don't have the appropriate privileges 231  */ 232  @CheckForNull 233  Class<?> loadFinalizer(); 234  } 235  236  /** 237  * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path, 238  * we needn't create a separate loader. 239  */ 240  static class SystemLoader implements FinalizerLoader { 241  // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable 242  // finding Finalizer on the system class path even if it is there. 243  @VisibleForTesting static boolean disabled; 244  245  @Override 246  @CheckForNull 247  public Class<?> loadFinalizer() { 248  if (disabled) { 249  return null; 250  } 251  ClassLoader systemLoader; 252  try { 253  systemLoader = ClassLoader.getSystemClassLoader(); 254  } catch (SecurityException e) { 255  logger.info("Not allowed to access system class loader."); 256  return null; 257  } 258  if (systemLoader != null) { 259  try { 260  return systemLoader.loadClass(FINALIZER_CLASS_NAME); 261  } catch (ClassNotFoundException e) { 262  // Ignore. Finalizer is simply in a child class loader. 263  return null; 264  } 265  } else { 266  return null; 267  } 268  } 269  } 270  271  /** 272  * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to 273  * our class loader (which could be that of a dynamically loaded web application or OSGi bundle), 274  * it would prevent our class loader from getting garbage collected. 275  */ 276  static class DecoupledLoader implements FinalizerLoader { 277  private static final String LOADING_ERROR = 278  "Could not load Finalizer in its own class loader. Loading Finalizer in the current class " 279  + "loader instead. As a result, you will not be able to garbage collect this class " 280  + "loader. To support reclaiming this class loader, either resolve the underlying " 281  + "issue, or move Guava to your system class path."; 282  283  @Override 284  @CheckForNull 285  public Class<?> loadFinalizer() { 286  try { 287  /* 288  * We use URLClassLoader because it's the only concrete class loader implementation in the 289  * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this 290  * class loader: 291  * 292  * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader 293  * 294  * System class loader will (and must) be the parent. 295  */ 296  ClassLoader finalizerLoader = newLoader(getBaseUrl()); 297  return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); 298  } catch (Exception e) { 299  logger.log(Level.WARNING, LOADING_ERROR, e); 300  return null; 301  } 302  } 303  304  /** Gets URL for base of path containing Finalizer.class. */ 305  URL getBaseUrl() throws IOException { 306  // Find URL pointing to Finalizer.class file. 307  String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; 308  URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); 309  if (finalizerUrl == null) { 310  throw new FileNotFoundException(finalizerPath); 311  } 312  313  // Find URL pointing to base of class path. 314  String urlString = finalizerUrl.toString(); 315  if (!urlString.endsWith(finalizerPath)) { 316  throw new IOException("Unsupported path style: " + urlString); 317  } 318  urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); 319  return new URL(finalizerUrl, urlString); 320  } 321  322  /** Creates a class loader with the given base URL as its classpath. */ 323  URLClassLoader newLoader(URL base) { 324  // We use the bootstrap class loader as the parent because Finalizer by design uses 325  // only standard Java classes. That also means that FinalizableReferenceQueueTest 326  // doesn't pick up the wrong version of the Finalizer class. 327  return new URLClassLoader(new URL[] {base}, null); 328  } 329  } 330  331  /** 332  * Loads Finalizer directly using the current class loader. We won't be able to garbage collect 333  * this class loader, but at least the world doesn't end. 334  */ 335  static class DirectLoader implements FinalizerLoader { 336  @Override 337  public Class<?> loadFinalizer() { 338  try { 339  return Class.forName(FINALIZER_CLASS_NAME); 340  } catch (ClassNotFoundException e) { 341  throw new AssertionError(e); 342  } 343  } 344  } 345  346  /** Looks up Finalizer.startFinalizer(). */ 347  static Method getStartFinalizer(Class<?> finalizer) { 348  try { 349  return finalizer.getMethod( 350  "startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class); 351  } catch (NoSuchMethodException e) { 352  throw new AssertionError(e); 353  } 354  } 355 }