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 static java.util.Objects.requireNonNull; 018 019import java.lang.ref.PhantomReference; 020import java.lang.ref.Reference; 021import java.lang.ref.ReferenceQueue; 022import java.lang.ref.WeakReference; 023import java.lang.reflect.Constructor; 024import java.lang.reflect.Field; 025import java.lang.reflect.Method; 026import java.util.logging.Level; 027import java.util.logging.Logger; 028import org.jspecify.annotations.Nullable; 029 030/** 031 * Thread that finalizes referents. All references should implement {@code 032 * com.google.common.base.FinalizableReference}. 033 * 034 * <p>While this class is public, we consider it to be *internal* and not part of our published API. 035 * It is public so we can access it reflectively across class loaders in secure environments. 036 * 037 * <p>This class can't depend on other Guava code. If we were to load this class in the same class 038 * loader as the rest of Guava, this thread would keep an indirect strong reference to the class 039 * loader and prevent it from being garbage collected. This poses a problem for environments where 040 * you want to throw away the class loader. For example, dynamically reloading a web application or 041 * unloading an OSGi bundle. 042 * 043 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class 044 * loader. That way, this class doesn't prevent the main class loader from getting garbage 045 * collected, and this class can detect when the main class loader has been garbage collected and 046 * stop itself. 047 */ 048public final class Finalizer implements Runnable { 049 050 private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); 051 052 /** 053 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively. 054 * 055 * @param finalizableReferenceClass FinalizableReference.class. 056 * @param queue a reference queue that the thread will poll. 057 * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued 058 * either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its 059 * close() method is called. 060 */ 061 public static void startFinalizer( 062 Class<?> finalizableReferenceClass, 063 ReferenceQueue<Object> queue, 064 PhantomReference<Object> frqReference) { 065 /* 066 * We use finalizableReferenceClass for two things: 067 * 068 * 1) To invoke FinalizableReference.finalizeReferent() 069 * 070 * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which 071 * point, Finalizer can stop running 072 */ 073 074 Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); 075 String threadName = "com.google.common.base.internal.Finalizer"; 076 Thread thread = null; 077 if (bigThreadConstructor != null) { 078 try { 079 boolean inheritThreadLocals = false; 080 long defaultStackSize = 0; 081 thread = 082 bigThreadConstructor.newInstance( 083 (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals); 084 } catch (Throwable t) { 085 logger.log( 086 Level.INFO, "Failed to create a thread without inherited thread-local values", t); 087 } 088 } 089 if (thread == null) { 090 thread = new Thread((ThreadGroup) null, finalizer, threadName); 091 } 092 thread.setDaemon(true); 093 094 try { 095 if (inheritableThreadLocals != null) { 096 inheritableThreadLocals.set(thread, null); 097 } 098 } catch (Throwable t) { 099 logger.log( 100 Level.INFO, 101 "Failed to clear thread local values inherited by reference finalizer thread.", 102 t); 103 } 104 105 thread.start(); 106 } 107 108 private final WeakReference<Class<?>> finalizableReferenceClassReference; 109 private final PhantomReference<Object> frqReference; 110 private final ReferenceQueue<Object> queue; 111 112 // By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter. 113 // But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread 114 // is created, by accessing a private field. 115 private static final @Nullable Constructor<Thread> bigThreadConstructor = 116 getBigThreadConstructor(); 117 118 private static final @Nullable Field inheritableThreadLocals = 119 (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null; 120 121 /** Constructs a new finalizer thread. */ 122 private Finalizer( 123 Class<?> finalizableReferenceClass, 124 ReferenceQueue<Object> queue, 125 PhantomReference<Object> frqReference) { 126 this.queue = queue; 127 128 this.finalizableReferenceClassReference = new WeakReference<>(finalizableReferenceClass); 129 130 // Keep track of the FRQ that started us so we know when to stop. 131 this.frqReference = frqReference; 132 } 133 134 /** Loops continuously, pulling references off the queue and cleaning them up. */ 135 @SuppressWarnings("InfiniteLoopStatement") 136 @Override 137 public void run() { 138 while (true) { 139 try { 140 if (!cleanUp(queue.remove())) { 141 break; 142 } 143 } catch (InterruptedException e) { 144 // ignore 145 } 146 } 147 } 148 149 /** 150 * Cleans up the given reference and any other references already in the queue. Catches and logs 151 * all throwables. 152 * 153 * @return true if the caller should continue to wait for more references to be added to the 154 * queue, false if the associated FinalizableReferenceQueue is no longer referenced. 155 */ 156 private boolean cleanUp(Reference<?> firstReference) { 157 Method finalizeReferentMethod = getFinalizeReferentMethod(); 158 if (finalizeReferentMethod == null) { 159 return false; 160 } 161 162 if (!finalizeReference(firstReference, finalizeReferentMethod)) { 163 return false; 164 } 165 166 /* 167 * Loop as long as we have references available so as not to waste CPU looking up the Method 168 * over and over again. 169 */ 170 while (true) { 171 Reference<?> furtherReference = queue.poll(); 172 if (furtherReference == null) { 173 return true; 174 } 175 if (!finalizeReference(furtherReference, finalizeReferentMethod)) { 176 return false; 177 } 178 } 179 } 180 181 /** 182 * Cleans up the given reference. Catches and logs all throwables. 183 * 184 * @return true if the caller should continue to clean up references from the queue, false if the 185 * associated FinalizableReferenceQueue is no longer referenced. 186 */ 187 private boolean finalizeReference(Reference<?> reference, Method finalizeReferentMethod) { 188 /* 189 * This is for the benefit of phantom references. Weak and soft references will have already 190 * been cleared by this point. 191 */ 192 reference.clear(); 193 194 if (reference == frqReference) { 195 /* 196 * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. 197 */ 198 return false; 199 } 200 201 try { 202 finalizeReferentMethod.invoke(reference); 203 } catch (Throwable t) { 204 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 205 } 206 return true; 207 } 208 209 /** Looks up FinalizableReference.finalizeReferent() method. */ 210 private @Nullable Method getFinalizeReferentMethod() { 211 Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get(); 212 if (finalizableReferenceClass == null) { 213 /* 214 * FinalizableReference's class loader was reclaimed. While there's a chance that other 215 * finalizable references could be enqueued subsequently (at which point the class loader 216 * would be resurrected by virtue of us having a strong reference to it), we should pretty 217 * much just shut down and make sure we don't keep it alive any longer than necessary. 218 */ 219 return null; 220 } 221 try { 222 return finalizableReferenceClass.getMethod("finalizeReferent"); 223 } catch (NoSuchMethodException e) { 224 throw new AssertionError(e); 225 } 226 } 227 228 private static @Nullable Field getInheritableThreadLocalsField() { 229 if (isAndroid()) { 230 /* 231 * We need not worry about class unloading under Android. Plus, this approach doesn't always 232 * work under Android. 233 * 234 * (Clearing inheritableThreadLocals could still be useful independent of class unloading, but 235 * that's a larger problem, which we don't attempt to solve anywhere else in Guava.) 236 */ 237 return null; 238 } 239 try { 240 Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); 241 inheritableThreadLocals.setAccessible(true); 242 return inheritableThreadLocals; 243 } catch (Throwable t) { 244 logger.log( 245 Level.INFO, 246 "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will " 247 + "inherit thread local values."); 248 return null; 249 } 250 } 251 252 private static @Nullable Constructor<Thread> getBigThreadConstructor() { 253 try { 254 return Thread.class.getConstructor( 255 ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); 256 } catch (Throwable t) { 257 // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals. 258 return null; 259 } 260 } 261 262 private static boolean isAndroid() { 263 return requireNonNull(System.getProperty("java.runtime.name", "")).contains("Android"); 264 } 265}