Coverage Summary for Class: Finalizer (com.google.common.base.internal)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| Finalizer | 0% (0/1) | 0% (0/8) | 0% (0/61) |
1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.base.internal; 16 17 import java.lang.ref.PhantomReference; 18 import java.lang.ref.Reference; 19 import java.lang.ref.ReferenceQueue; 20 import java.lang.ref.WeakReference; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Field; 23 import java.lang.reflect.Method; 24 import java.util.logging.Level; 25 import java.util.logging.Logger; 26 import org.checkerframework.checker.nullness.qual.Nullable; 27 28 /** 29 * Thread that finalizes referents. All references should implement {@code 30 * com.google.common.base.FinalizableReference}. 31 * 32 * <p>While this class is public, we consider it to be *internal* and not part of our published API. 33 * It is public so we can access it reflectively across class loaders in secure environments. 34 * 35 * <p>This class can't depend on other Guava code. If we were to load this class in the same class 36 * loader as the rest of Guava, this thread would keep an indirect strong reference to the class 37 * loader and prevent it from being garbage collected. This poses a problem for environments where 38 * you want to throw away the class loader. For example, dynamically reloading a web application or 39 * unloading an OSGi bundle. 40 * 41 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class 42 * loader. That way, this class doesn't prevent the main class loader from getting garbage 43 * collected, and this class can detect when the main class loader has been garbage collected and 44 * stop itself. 45 */ 46 public class Finalizer implements Runnable { 47 48 private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); 49 50 /** Name of FinalizableReference.class. */ 51 private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference"; 52 53 /** 54 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively. 55 * 56 * @param finalizableReferenceClass FinalizableReference.class. 57 * @param queue a reference queue that the thread will poll. 58 * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued 59 * either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its 60 * close() method is called. 61 */ 62 public static void startFinalizer( 63 Class<?> finalizableReferenceClass, 64 ReferenceQueue<Object> queue, 65 PhantomReference<Object> frqReference) { 66 /* 67 * We use FinalizableReference.class for two things: 68 * 69 * 1) To invoke FinalizableReference.finalizeReferent() 70 * 71 * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which 72 * point, Finalizer can stop running 73 */ 74 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { 75 throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + "."); 76 } 77 78 Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); 79 String threadName = Finalizer.class.getName(); 80 Thread thread = null; 81 if (bigThreadConstructor != null) { 82 try { 83 boolean inheritThreadLocals = false; 84 long defaultStackSize = 0; 85 thread = 86 bigThreadConstructor.newInstance( 87 (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals); 88 } catch (Throwable t) { 89 logger.log( 90 Level.INFO, "Failed to create a thread without inherited thread-local values", t); 91 } 92 } 93 if (thread == null) { 94 thread = new Thread((ThreadGroup) null, finalizer, threadName); 95 } 96 thread.setDaemon(true); 97 98 try { 99 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 @Nullable Constructor<Thread> bigThreadConstructor = 120 getBigThreadConstructor(); 121 122 private static final @Nullable 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 @Nullable 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 @Nullable 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 @Nullable 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 }