1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.base.internal; 18 19 import java.lang.ref.PhantomReference; 20 import java.lang.ref.Reference; 21 import java.lang.ref.ReferenceQueue; 22 import java.lang.ref.WeakReference; 23 import java.lang.reflect.Field; 24 import java.lang.reflect.Method; 25 import java.util.logging.Level; 26 import java.util.logging.Logger; 27 28 /** 29 * Thread that finalizes referents. All references should implement 30 * {@code com.google.common.base.FinalizableReference}. 31 * 32 * <p>While this class is public, we consider it to be *internal* and not part 33 * of our published API. It is public so we can access it reflectively across 34 * class loaders in secure environments. 35 * 36 * <p>This class can't depend on other Google Collections code. If we were 37 * to load this class in the same class loader as the rest of 38 * Google Collections, this thread would keep an indirect strong reference 39 * to the class loader and prevent it from being garbage collected. This 40 * poses a problem for environments where you want to throw away the class 41 * loader. For example, dynamically reloading a web application or unloading 42 * an OSGi bundle. 43 * 44 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class 45 * in its own class loader. That way, this class doesn't prevent the main 46 * class loader from getting garbage collected, and this class can detect when 47 * the main class loader has been garbage collected and stop itself. 48 */ 49 public class Finalizer implements Runnable { 50 51 private static final Logger logger 52 = Logger.getLogger(Finalizer.class.getName()); 53 54 /** Name of FinalizableReference.class. */ 55 private static final String FINALIZABLE_REFERENCE 56 = "com.google.common.base.FinalizableReference"; 57 58 /** 59 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method 60 * reflectively. 61 * 62 * @param finalizableReferenceClass FinalizableReference.class. 63 * @param queue a reference queue that the thread will poll. 64 * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be 65 * queued either when the FinalizableReferenceQueue is no longer referenced anywhere, or when 66 * its close() method is called. 67 */ startFinalizer( Class<?> finalizableReferenceClass, ReferenceQueue<Object> queue, PhantomReference<Object> frqReference)68 public static void startFinalizer( 69 Class<?> finalizableReferenceClass, 70 ReferenceQueue<Object> queue, 71 PhantomReference<Object> frqReference) { 72 /* 73 * We use FinalizableReference.class for two things: 74 * 75 * 1) To invoke FinalizableReference.finalizeReferent() 76 * 77 * 2) To detect when FinalizableReference's class loader has to be garbage 78 * collected, at which point, Finalizer can stop running 79 */ 80 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { 81 throw new IllegalArgumentException( 82 "Expected " + FINALIZABLE_REFERENCE + "."); 83 } 84 85 Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); 86 Thread thread = new Thread(finalizer); 87 thread.setName(Finalizer.class.getName()); 88 thread.setDaemon(true); 89 90 try { 91 if (inheritableThreadLocals != null) { 92 inheritableThreadLocals.set(thread, null); 93 } 94 } catch (Throwable t) { 95 logger.log(Level.INFO, "Failed to clear thread local values inherited" 96 + " by reference finalizer thread.", t); 97 } 98 99 thread.start(); 100 } 101 102 private final WeakReference<Class<?>> finalizableReferenceClassReference; 103 private final PhantomReference<Object> frqReference; 104 private final ReferenceQueue<Object> queue; 105 106 private static final Field inheritableThreadLocals 107 = getInheritableThreadLocalsField(); 108 109 /** Constructs a new finalizer thread. */ Finalizer( Class<?> finalizableReferenceClass, ReferenceQueue<Object> queue, PhantomReference<Object> frqReference)110 private Finalizer( 111 Class<?> finalizableReferenceClass, 112 ReferenceQueue<Object> queue, 113 PhantomReference<Object> frqReference) { 114 this.queue = queue; 115 116 this.finalizableReferenceClassReference 117 = new WeakReference<Class<?>>(finalizableReferenceClass); 118 119 // Keep track of the FRQ that started us so we know when to stop. 120 this.frqReference = frqReference; 121 } 122 123 /** 124 * Loops continuously, pulling references off the queue and cleaning them up. 125 */ 126 @SuppressWarnings("InfiniteLoopStatement") 127 @Override run()128 public void run() { 129 while (true) { 130 try { 131 if (!cleanUp(queue.remove())) { 132 break; 133 } 134 } catch (InterruptedException e) { /* ignore */ } 135 } 136 } 137 138 /** 139 * Cleans up a single reference. Catches and logs all throwables. 140 * @return true if the caller should continue, false if the associated FinalizableReferenceQueue 141 * is no longer referenced. 142 */ cleanUp(Reference<?> reference)143 private boolean cleanUp(Reference<?> reference) { 144 Method finalizeReferentMethod = getFinalizeReferentMethod(); 145 if (finalizeReferentMethod == null) { 146 return false; 147 } 148 do { 149 /* 150 * This is for the benefit of phantom references. Weak and soft 151 * references will have already been cleared by this point. 152 */ 153 reference.clear(); 154 155 if (reference == frqReference) { 156 /* 157 * The client no longer has a reference to the 158 * FinalizableReferenceQueue. We can stop. 159 */ 160 return false; 161 } 162 163 try { 164 finalizeReferentMethod.invoke(reference); 165 } catch (Throwable t) { 166 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 167 } 168 169 /* 170 * Loop as long as we have references available so as not to waste 171 * CPU looking up the Method over and over again. 172 */ 173 } while ((reference = queue.poll()) != null); 174 return true; 175 } 176 177 /** 178 * Looks up FinalizableReference.finalizeReferent() method. 179 */ getFinalizeReferentMethod()180 private Method getFinalizeReferentMethod() { 181 Class<?> finalizableReferenceClass 182 = finalizableReferenceClassReference.get(); 183 if (finalizableReferenceClass == null) { 184 /* 185 * FinalizableReference's class loader was reclaimed. While there's a 186 * chance that other finalizable references could be enqueued 187 * subsequently (at which point the class loader would be resurrected 188 * by virtue of us having a strong reference to it), we should pretty 189 * much just shut down and make sure we don't keep it alive any longer 190 * than necessary. 191 */ 192 return null; 193 } 194 try { 195 return finalizableReferenceClass.getMethod("finalizeReferent"); 196 } catch (NoSuchMethodException e) { 197 throw new AssertionError(e); 198 } 199 } 200 getInheritableThreadLocalsField()201 public static Field getInheritableThreadLocalsField() { 202 try { 203 Field inheritableThreadLocals 204 = Thread.class.getDeclaredField("inheritableThreadLocals"); 205 inheritableThreadLocals.setAccessible(true); 206 return inheritableThreadLocals; 207 } catch (Throwable t) { 208 logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals." 209 + " Reference finalizer threads will inherit thread local" 210 + " values."); 211 return null; 212 } 213 } 214 } 215