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

Class Class, % Method, % Line, %
Finalizer 0% (0/1) 0% (0/8) 0% (0/61)


1 /* 2  * Copyright (C) 2008 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.internal; 16  17 import java.lang.ref.PhantomReference; 18 import java.lang.ref.Reference; 19 import java.lang.ref.ReferenceQueue; 20 import java.lang.ref.WeakReference; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Field; 23 import java.lang.reflect.Method; 24 import java.util.logging.Level; 25 import java.util.logging.Logger; 26 import org.checkerframework.checker.nullness.qual.Nullable; 27  28 /** 29  * Thread that finalizes referents. All references should implement {@code 30  * com.google.common.base.FinalizableReference}. 31  * 32  * <p>While this class is public, we consider it to be *internal* and not part of our published API. 33  * It is public so we can access it reflectively across class loaders in secure environments. 34  * 35  * <p>This class can't depend on other Guava code. If we were to load this class in the same class 36  * loader as the rest of Guava, this thread would keep an indirect strong reference to the class 37  * loader and prevent it from being garbage collected. This poses a problem for environments where 38  * you want to throw away the class loader. For example, dynamically reloading a web application or 39  * unloading an OSGi bundle. 40  * 41  * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class 42  * loader. That way, this class doesn't prevent the main class loader from getting garbage 43  * collected, and this class can detect when the main class loader has been garbage collected and 44  * stop itself. 45  */ 46 public class Finalizer implements Runnable { 47  48  private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); 49  50  /** Name of FinalizableReference.class. */ 51  private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference"; 52  53  /** 54  * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively. 55  * 56  * @param finalizableReferenceClass FinalizableReference.class. 57  * @param queue a reference queue that the thread will poll. 58  * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued 59  * either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its 60  * close() method is called. 61  */ 62  public static void startFinalizer( 63  Class<?> finalizableReferenceClass, 64  ReferenceQueue<Object> queue, 65  PhantomReference<Object> frqReference) { 66  /* 67  * We use FinalizableReference.class for two things: 68  * 69  * 1) To invoke FinalizableReference.finalizeReferent() 70  * 71  * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which 72  * point, Finalizer can stop running 73  */ 74  if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { 75  throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + "."); 76  } 77  78  Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); 79  String threadName = Finalizer.class.getName(); 80  Thread thread = null; 81  if (bigThreadConstructor != null) { 82  try { 83  boolean inheritThreadLocals = false; 84  long defaultStackSize = 0; 85  thread = 86  bigThreadConstructor.newInstance( 87  (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals); 88  } catch (Throwable t) { 89  logger.log( 90  Level.INFO, "Failed to create a thread without inherited thread-local values", t); 91  } 92  } 93  if (thread == null) { 94  thread = new Thread((ThreadGroup) null, finalizer, threadName); 95  } 96  thread.setDaemon(true); 97  98  try { 99  if (inheritableThreadLocals != null) { 100  inheritableThreadLocals.set(thread, null); 101  } 102  } catch (Throwable t) { 103  logger.log( 104  Level.INFO, 105  "Failed to clear thread local values inherited by reference finalizer thread.", 106  t); 107  } 108  109  thread.start(); 110  } 111  112  private final WeakReference<Class<?>> finalizableReferenceClassReference; 113  private final PhantomReference<Object> frqReference; 114  private final ReferenceQueue<Object> queue; 115  116  // By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter. 117  // But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread 118  // is created, by accessing a private field. 119  private static final @Nullable Constructor<Thread> bigThreadConstructor = 120  getBigThreadConstructor(); 121  122  private static final @Nullable Field inheritableThreadLocals = 123  (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null; 124  125  /** Constructs a new finalizer thread. */ 126  private Finalizer( 127  Class<?> finalizableReferenceClass, 128  ReferenceQueue<Object> queue, 129  PhantomReference<Object> frqReference) { 130  this.queue = queue; 131  132  this.finalizableReferenceClassReference = 133  new WeakReference<Class<?>>(finalizableReferenceClass); 134  135  // Keep track of the FRQ that started us so we know when to stop. 136  this.frqReference = frqReference; 137  } 138  139  /** Loops continuously, pulling references off the queue and cleaning them up. */ 140  @SuppressWarnings("InfiniteLoopStatement") 141  @Override 142  public void run() { 143  while (true) { 144  try { 145  if (!cleanUp(queue.remove())) { 146  break; 147  } 148  } catch (InterruptedException e) { 149  // ignore 150  } 151  } 152  } 153  154  /** 155  * Cleans up a single reference. Catches and logs all throwables. 156  * 157  * @return true if the caller should continue, false if the associated FinalizableReferenceQueue 158  * is no longer referenced. 159  */ 160  private boolean cleanUp(Reference<?> reference) { 161  Method finalizeReferentMethod = getFinalizeReferentMethod(); 162  if (finalizeReferentMethod == null) { 163  return false; 164  } 165  do { 166  /* 167  * This is for the benefit of phantom references. Weak and soft references will have already 168  * been cleared by this point. 169  */ 170  reference.clear(); 171  172  if (reference == frqReference) { 173  /* 174  * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. 175  */ 176  return false; 177  } 178  179  try { 180  finalizeReferentMethod.invoke(reference); 181  } catch (Throwable t) { 182  logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 183  } 184  185  /* 186  * Loop as long as we have references available so as not to waste CPU looking up the Method 187  * over and over again. 188  */ 189  } while ((reference = queue.poll()) != null); 190  return true; 191  } 192  193  /** Looks up FinalizableReference.finalizeReferent() method. */ 194  private @Nullable Method getFinalizeReferentMethod() { 195  Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get(); 196  if (finalizableReferenceClass == null) { 197  /* 198  * FinalizableReference's class loader was reclaimed. While there's a chance that other 199  * finalizable references could be enqueued subsequently (at which point the class loader 200  * would be resurrected by virtue of us having a strong reference to it), we should pretty 201  * much just shut down and make sure we don't keep it alive any longer than necessary. 202  */ 203  return null; 204  } 205  try { 206  return finalizableReferenceClass.getMethod("finalizeReferent"); 207  } catch (NoSuchMethodException e) { 208  throw new AssertionError(e); 209  } 210  } 211  212  private static @Nullable Field getInheritableThreadLocalsField() { 213  try { 214  Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); 215  inheritableThreadLocals.setAccessible(true); 216  return inheritableThreadLocals; 217  } catch (Throwable t) { 218  logger.log( 219  Level.INFO, 220  "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will " 221  + "inherit thread local values."); 222  return null; 223  } 224  } 225  226  private static @Nullable Constructor<Thread> getBigThreadConstructor() { 227  try { 228  return Thread.class.getConstructor( 229  ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); 230  } catch (Throwable t) { 231  // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals. 232  return null; 233  } 234  } 235 }