1 /* 2 * Copyright (C) 2008 Google Inc. 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 extends Thread { 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 frq reference to instance of FinalizableReferenceQueue that started 64 * this thread 65 * @return ReferenceQueue which Finalizer will poll 66 */ 67 public static ReferenceQueue<Object> startFinalizer( 68 Class<?> finalizableReferenceClass, Object frq) { 69 /* 70 * We use FinalizableReference.class for two things: 71 * 72 * 1) To invoke FinalizableReference.finalizeReferent() 73 * 74 * 2) To detect when FinalizableReference's class loader has to be garbage 75 * collected, at which point, Finalizer can stop running 76 */ 77 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { 78 throw new IllegalArgumentException( 79 "Expected " + FINALIZABLE_REFERENCE + "."); 80 } 81 82 Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq); 83 finalizer.start(); 84 return finalizer.queue; 85 } 86 87 private final WeakReference<Class<?>> finalizableReferenceClassReference; 88 private final PhantomReference<Object> frqReference; 89 private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); 90 91 private static final Field inheritableThreadLocals 92 = getInheritableThreadLocalsField(); 93 94 /** Constructs a new finalizer thread. */ 95 private Finalizer(Class<?> finalizableReferenceClass, Object frq) { 96 super(Finalizer.class.getName()); 97 98 this.finalizableReferenceClassReference 99 = new WeakReference<Class<?>>(finalizableReferenceClass); 100 101 // Keep track of the FRQ that started us so we know when to stop. 102 this.frqReference = new PhantomReference<Object>(frq, queue); 103 104 setDaemon(true); 105 106 try { 107 if (inheritableThreadLocals != null) { 108 inheritableThreadLocals.set(this, null); 109 } 110 } catch (Throwable t) { 111 logger.log(Level.INFO, "Failed to clear thread local values inherited" 112 + " by reference finalizer thread.", t); 113 } 114 115 // TODO: Priority? 116 } 117 118 /** 119 * Loops continuously, pulling references off the queue and cleaning them up. 120 */ 121 @SuppressWarnings("InfiniteLoopStatement") 122 @Override 123 public void run() { 124 try { 125 while (true) { 126 try { 127 cleanUp(queue.remove()); 128 } catch (InterruptedException e) { /* ignore */ } 129 } 130 } catch (ShutDown shutDown) { /* ignore */ } 131 } 132 133 /** 134 * Cleans up a single reference. Catches and logs all throwables. 135 */ 136 private void cleanUp(Reference<?> reference) throws ShutDown { 137 Method finalizeReferentMethod = getFinalizeReferentMethod(); 138 do { 139 /* 140 * This is for the benefit of phantom references. Weak and soft 141 * references will have already been cleared by this point. 142 */ 143 reference.clear(); 144 145 if (reference == frqReference) { 146 /* 147 * The client no longer has a reference to the 148 * FinalizableReferenceQueue. We can stop. 149 */ 150 throw new ShutDown(); 151 } 152 153 try { 154 finalizeReferentMethod.invoke(reference); 155 } catch (Throwable t) { 156 logger.log(Level.SEVERE, "Error cleaning up after reference.", t); 157 } 158 159 /* 160 * Loop as long as we have references available so as not to waste 161 * CPU looking up the Method over and over again. 162 */ 163 } while ((reference = queue.poll()) != null); 164 } 165 166 /** 167 * Looks up FinalizableReference.finalizeReferent() method. 168 */ 169 private Method getFinalizeReferentMethod() throws ShutDown { 170 Class<?> finalizableReferenceClass 171 = finalizableReferenceClassReference.get(); 172 if (finalizableReferenceClass == null) { 173 /* 174 * FinalizableReference's class loader was reclaimed. While there's a 175 * chance that other finalizable references could be enqueued 176 * subsequently (at which point the class loader would be resurrected 177 * by virtue of us having a strong reference to it), we should pretty 178 * much just shut down and make sure we don't keep it alive any longer 179 * than necessary. 180 */ 181 throw new ShutDown(); 182 } 183 try { 184 return finalizableReferenceClass.getMethod("finalizeReferent"); 185 } catch (NoSuchMethodException e) { 186 throw new AssertionError(e); 187 } 188 } 189 190 public static Field getInheritableThreadLocalsField() { 191 try { 192 Field inheritableThreadLocals 193 = Thread.class.getDeclaredField("inheritableThreadLocals"); 194 inheritableThreadLocals.setAccessible(true); 195 return inheritableThreadLocals; 196 } catch (Throwable t) { 197 logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals." 198 + " Reference finalizer threads will inherit thread local" 199 + " values."); 200 return null; 201 } 202 } 203 204 /** Indicates that it's time to shut down the Finalizer. */ 205 @SuppressWarnings("serial") // Never serialized or thrown out of this class. 206 private static class ShutDown extends Exception { } 207 } 208