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; 026import javax.annotation.CheckForNull; 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 */ 046// no @ElementTypesAreNonNullByDefault for the reasons discussed above 047public class Finalizer implements Runnable { 048 049 private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); 050 051 /** Name of FinalizableReference.class. */ 052 private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference"; 053 054 /** 055 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively. 056 * 057 * @param finalizableReferenceClass FinalizableReference.class. 058 * @param queue a reference queue that the thread will poll. 059 * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued 060 * either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its 061 * close() method is called. 062 */ 063 public static void startFinalizer( 064 Class<?> finalizableReferenceClass, 065 ReferenceQueue<Object> queue, 066 PhantomReference<Object> frqReference) { 067 /* 068 * We use FinalizableReference.class for two things: 069 * 070 * 1) To invoke FinalizableReference.finalizeReferent() 071 * 072 * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which 073 * point, Finalizer can stop running 074 */ 075 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { 076 throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + "."); 077 } 078 079 Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); 080 String threadName = Finalizer.class.getName(); 081 Thread thread = null; 082 if (bigThreadConstructor != null) { 083 try { 084 boolean inheritThreadLocals = false; 085 long defaultStackSize = 0; 086 thread = 087 bigThreadConstructor.newInstance( 088 (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals); 089 } catch (Throwable t) { 090 logger.log( 091 Level.INFO, "Failed to create a thread without inherited thread-local values", t); 092 } 093 } 094 if (thread == null) { 095 thread = new Thread((ThreadGroup) null, finalizer, threadName); 096 } 097 thread.setDaemon(true); 098 099 try { 100 if (inheritableThreadLocals != null) { 101 inheritableThreadLocals.set(thread, null); 102 } 103 } catch (Throwable t) { 104 logger.log( 105 Level.INFO, 106 "Failed to clear thread local values inherited by reference finalizer thread.", 107 t); 108 } 109 110 thread.start(); 111 } 112 113 private final WeakReference<Class<?>> finalizableReferenceClassReference; 114 private final PhantomReference<Object> frqReference; 115 private final ReferenceQueue<Object> queue; 116 117 // By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter. 118 // But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread 119 // is created, by accessing a private field. 120 @CheckForNull 121 private static final Constructor<Thread> bigThreadConstructor = getBigThreadConstructor(); 122 123 @CheckForNull 124 private static final Field inheritableThreadLocals = 125 (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null; 126 127 /** Constructs a new finalizer thread. */ 128 private Finalizer( 129 Class<?> finalizableReferenceClass, 130 ReferenceQueue<Object> queue, 131 PhantomReference<Object> frqReference) { 132 this.queue = queue; 133 134 this.finalizableReferenceClassReference = new WeakReference<>(finalizableReferenceClass); 135 136 // Keep track of the FRQ that started us so we know when to stop. 137 this.frqReference = frqReference; 138 } 139 140 /** Loops continuously, pulling references off the queue and cleaning them up. */ 141 @SuppressWarnings("InfiniteLoopStatement") 142 @Override 143 public void run() { 144 while (true) { 145 try { 146 if (!cleanUp(queue.remove())) { 147 break; 148 } 149 } catch (InterruptedException e) { 150 // ignore 151 } 152 } 153 } 154 155 /** 156 * Cleans up the given reference and any other references already in the queue. Catches and logs 157 * all throwables. 158 * 159 * @return true if the caller should continue to wait for more references to be added to the 160 * queue, false if the associated FinalizableReferenceQueue is no longer referenced. 161 */ 162 private boolean cleanUp(Reference<?> firstReference) { 163 Method finalizeReferentMethod = getFinalizeReferentMethod(); 164 if (finalizeReferentMethod == null) { 165 return false; 166 } 167 168 if (!finalizeReference(firstReference, finalizeReferentMethod)) { 169 return false; 170 } 171 172 /* 173 * Loop as long as we have references available so as not to waste CPU looking up the Method 174 * over and over again. 175 */ 176 while (true) { 177 Reference<?> furtherReference = queue.poll(); 178 if (furtherReference == null) { 179 return true; 180 } 181 if (!finalizeReference(furtherReference, finalizeReferentMethod)) { 182 return false; 183 } 184 } 185 } 186 187 /** 188 * Cleans up the given reference. Catches and logs all throwables. 189 * 190 * @return true if the caller should continue to clean up references from the queue, false if the 191 * associated FinalizableReferenceQueue is no longer referenced. 192 */ 193 private boolean finalizeReference(Reference<?> reference, Method finalizeReferentMethod) { 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 200 if (reference == frqReference) { 201 /* 202 * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. 203 */ 204 return false; 205 } 206 207 try { 208 finalizeReferentMethod.invoke(reference); 209 } catch (Throwable t) { 210 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 211 } 212 return true; 213 } 214 215 /** Looks up FinalizableReference.finalizeReferent() method. */ 216 @CheckForNull 217 private Method getFinalizeReferentMethod() { 218 Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get(); 219 if (finalizableReferenceClass == null) { 220 /* 221 * FinalizableReference's class loader was reclaimed. While there's a chance that other 222 * finalizable references could be enqueued subsequently (at which point the class loader 223 * would be resurrected by virtue of us having a strong reference to it), we should pretty 224 * much just shut down and make sure we don't keep it alive any longer than necessary. 225 */ 226 return null; 227 } 228 try { 229 return finalizableReferenceClass.getMethod("finalizeReferent"); 230 } catch (NoSuchMethodException e) { 231 throw new AssertionError(e); 232 } 233 } 234 235 @CheckForNull 236 private static Field getInheritableThreadLocalsField() { 237 try { 238 Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); 239 inheritableThreadLocals.setAccessible(true); 240 return inheritableThreadLocals; 241 } catch (Throwable t) { 242 logger.log( 243 Level.INFO, 244 "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will " 245 + "inherit thread local values."); 246 return null; 247 } 248 } 249 250 @CheckForNull 251 private static Constructor<Thread> getBigThreadConstructor() { 252 try { 253 return Thread.class.getConstructor( 254 ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); 255 } catch (Throwable t) { 256 // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals. 257 return null; 258 } 259 } 260}