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}