001/*
002 * Copyright (C) 2008 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.internal;
016
017import java.lang.ref.PhantomReference;
018import java.lang.ref.Reference;
019import java.lang.ref.ReferenceQueue;
020import java.lang.ref.WeakReference;
021import java.lang.reflect.Constructor;
022import java.lang.reflect.Field;
023import java.lang.reflect.Method;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026
027
028/**
029 * Thread that finalizes referents. All references should implement {@code
030 * com.google.common.base.FinalizableReference}.
031 *
032 * <p>While this class is public, we consider it to be *internal* and not part of our published API.
033 * It is public so we can access it reflectively across class loaders in secure environments.
034 *
035 * <p>This class can't depend on other Guava code. If we were to load this class in the same class
036 * loader as the rest of Guava, this thread would keep an indirect strong reference to the class
037 * loader and prevent it from being garbage collected. This poses a problem for environments where
038 * you want to throw away the class loader. For example, dynamically reloading a web application or
039 * unloading an OSGi bundle.
040 *
041 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class
042 * loader. That way, this class doesn't prevent the main class loader from getting garbage
043 * collected, and this class can detect when the main class loader has been garbage collected and
044 * stop itself.
045 */
046public class Finalizer implements Runnable {
047
048  private static final Logger logger = Logger.getLogger(Finalizer.class.getName());
049
050  /** Name of FinalizableReference.class. */
051  private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference";
052
053  /**
054   * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively.
055   *
056   * @param finalizableReferenceClass FinalizableReference.class.
057   * @param queue a reference queue that the thread will poll.
058   * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued
059   *     either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its
060   *     close() method is called.
061   */
062  public static void startFinalizer(
063      Class<?> finalizableReferenceClass,
064      ReferenceQueue<Object> queue,
065      PhantomReference<Object> frqReference) {
066    /*
067     * We use FinalizableReference.class for two things:
068     *
069     * 1) To invoke FinalizableReference.finalizeReferent()
070     *
071     * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which
072     * point, Finalizer can stop running
073     */
074    if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
075      throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + ".");
076    }
077
078    Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference);
079    String threadName = Finalizer.class.getName();
080    Thread thread = null;
081    if (bigThreadConstructor != null) {
082      try {
083        boolean inheritThreadLocals = false;
084        long defaultStackSize = 0;
085        thread =
086            bigThreadConstructor.newInstance(
087                (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals);
088      } catch (Throwable t) {
089        logger.log(
090            Level.INFO, "Failed to create a thread without inherited thread-local values", t);
091      }
092    }
093    if (thread == null) {
094      thread = new Thread((ThreadGroup) null, finalizer, threadName);
095    }
096    thread.setDaemon(true);
097
098    try {
099      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 Constructor<Thread> bigThreadConstructor =
120      getBigThreadConstructor();
121
122  private static final 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 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 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 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}