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 a single reference. Catches and logs all throwables. 157 * 158 * @return true if the caller should continue, false if the associated FinalizableReferenceQueue 159 * is no longer referenced. 160 */ 161 private boolean cleanUp(Reference<?> reference) { 162 Method finalizeReferentMethod = getFinalizeReferentMethod(); 163 if (finalizeReferentMethod == null) { 164 return false; 165 } 166 do { 167 /* 168 * This is for the benefit of phantom references. Weak and soft references will have already 169 * been cleared by this point. 170 */ 171 reference.clear(); 172 173 if (reference == frqReference) { 174 /* 175 * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. 176 */ 177 return false; 178 } 179 180 try { 181 finalizeReferentMethod.invoke(reference); 182 } catch (Throwable t) { 183 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 184 } 185 186 /* 187 * Loop as long as we have references available so as not to waste CPU looking up the Method 188 * over and over again. 189 */ 190 } while ((reference = queue.poll()) != null); 191 return true; 192 } 193 194 /** Looks up FinalizableReference.finalizeReferent() method. */ 195 @CheckForNull 196 private Method getFinalizeReferentMethod() { 197 Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get(); 198 if (finalizableReferenceClass == null) { 199 /* 200 * FinalizableReference's class loader was reclaimed. While there's a chance that other 201 * finalizable references could be enqueued subsequently (at which point the class loader 202 * would be resurrected by virtue of us having a strong reference to it), we should pretty 203 * much just shut down and make sure we don't keep it alive any longer than necessary. 204 */ 205 return null; 206 } 207 try { 208 return finalizableReferenceClass.getMethod("finalizeReferent"); 209 } catch (NoSuchMethodException e) { 210 throw new AssertionError(e); 211 } 212 } 213 214 @CheckForNull 215 private static Field getInheritableThreadLocalsField() { 216 try { 217 Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); 218 inheritableThreadLocals.setAccessible(true); 219 return inheritableThreadLocals; 220 } catch (Throwable t) { 221 logger.log( 222 Level.INFO, 223 "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will " 224 + "inherit thread local values."); 225 return null; 226 } 227 } 228 229 @CheckForNull 230 private static Constructor<Thread> getBigThreadConstructor() { 231 try { 232 return Thread.class.getConstructor( 233 ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); 234 } catch (Throwable t) { 235 // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals. 236 return null; 237 } 238 } 239}