1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.ref; 27 28 import java.security.PrivilegedAction; 29 import java.security.AccessController; 30 import java.util.Arrays; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 import sun.misc.JavaLangAccess; 35 import sun.misc.ManagedLocalsThread; 36 import sun.misc.SharedSecrets; 37 import sun.misc.VM; 38 39 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in 40 same package as the Reference 41 class */ 42 43 private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); 44 private static Finalizer unfinalized = null; 45 private static final Object lock = new Object(); 46 47 private Finalizer 48 next = null, 49 prev = null; 50 51 private boolean hasBeenFinalized() { 52 return (next == this); 53 } 54 55 private void add() { 56 synchronized (lock) { 57 if (unfinalized != null) { 58 this.next = unfinalized; 59 unfinalized.prev = this; 60 } 61 unfinalized = this; 62 } 63 } 64 65 private void remove() { 66 synchronized (lock) { 67 if (unfinalized == this) { 68 if (this.next != null) { 69 unfinalized = this.next; 70 } else { 71 unfinalized = this.prev; 72 } 73 } 74 if (this.next != null) { 75 this.next.prev = this.prev; 76 } 77 if (this.prev != null) { 78 this.prev.next = this.next; 79 } 80 this.next = this; /* Indicates that this has been finalized */ 81 this.prev = this; 82 } 83 } 84 85 private Finalizer(Object finalizee) { 86 super(finalizee, queue); 87 add(); 88 } 89 90 /* Invoked by VM */ 91 static void register(Object finalizee) { 92 new Finalizer(finalizee); 93 } 94 95 private void runFinalizer(JavaLangAccess jla) { 96 synchronized (this) { 97 if (hasBeenFinalized()) return; 98 remove(); 99 } 100 try { 101 Object finalizee = this.get(); 102 if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { 103 jla.invokeFinalize(finalizee); 104 105 /* Clear stack slot containing this variable, to decrease 106 the chances of false retention with a conservative GC */ 107 finalizee = null; 108 } 109 } catch (Throwable x) { } 110 super.clear(); 111 } 112 113 /* Create a privileged secondary finalizer thread in the system thread 114 group for the given Runnable, and wait for it to complete. 115 116 This method is used by both runFinalization and runFinalizersOnExit. 117 The former method invokes all pending finalizers, while the latter 118 invokes all uninvoked finalizers if on-exit finalization has been 119 enabled. 120 121 These two methods could have been implemented by offloading their work 122 to the regular finalizer thread and waiting for that thread to finish. 123 The advantage of creating a fresh thread, however, is that it insulates 124 invokers of these methods from a stalled or deadlocked finalizer thread. 125 */ 126 private static void forkSecondaryFinalizer(final Runnable proc) { 127 AccessController.doPrivileged( 128 new PrivilegedAction<>() { 129 public Void run() { 130 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 131 for (ThreadGroup tgn = tg; 132 tgn != null; 133 tg = tgn, tgn = tg.getParent()); 134 Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer"); 135 sft.start(); 136 try { 137 sft.join(); 138 } catch (InterruptedException x) { 139 Thread.currentThread().interrupt(); 140 } 141 return null; 142 }}); 143 } 144 145 /* Called by Runtime.runFinalization() */ 146 static void runFinalization() { 147 if (!VM.isBooted()) { 148 return; 149 } 150 151 forkSecondaryFinalizer(new Runnable() { 152 private volatile boolean running; 153 public void run() { 154 // in case of recursive call to run() 155 if (running) 156 return; 157 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 158 running = true; 159 for (;;) { 160 Finalizer f = (Finalizer)queue.poll(); 161 if (f == null) break; 162 f.runFinalizer(jla); 163 } 164 } 165 }); 166 } 167 168 /* Invoked by java.lang.Shutdown */ 169 static void runAllFinalizers() { 170 if (!VM.isBooted()) { 171 return; 172 } 173 174 forkSecondaryFinalizer(new Runnable() { 175 private volatile boolean running; 176 public void run() { 177 // in case of recursive call to run() 178 if (running) 179 return; 180 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 181 running = true; 182 for (;;) { 183 Finalizer f; 184 synchronized (lock) { 185 f = unfinalized; 186 if (f == null) break; 187 unfinalized = f.next; 188 } 189 f.runFinalizer(jla); 190 }}}); 191 } 192 193 static String printFinalizationQueue() { 194 Map<String, int[]> countMap = new HashMap<>(); 195 queue.forEach(r -> { 196 Object referent = r.get(); 197 if (referent != null) { 198 countMap.computeIfAbsent( 199 referent.getClass().getName(), k -> new int[1])[0]++; 200 /* Clear stack slot containing this variable, to decrease 201 the chances of false retention with a conservative GC */ 202 referent = null; 203 } 204 }); 205 @SuppressWarnings("unchecked") 206 Map.Entry<String, int[]>[] entries = countMap.entrySet().toArray( 207 new Map.Entry[countMap.size()] 208 ); 209 Arrays.sort(entries, (e1, e2) -> 210 Integer.compare(e2.getValue()[0], e1.getValue()[0])); 211 StringBuilder sb = new StringBuilder(); 212 for (Map.Entry<String, int[]> e : entries) { 213 sb.append("Class: ").append(e.getKey()) 214 .append(" count: ").append(e.getValue()[0]).append("\n"); 215 } 216 return sb.toString(); 217 } 218 219 private static class FinalizerThread extends ManagedLocalsThread { 220 private volatile boolean running; 221 FinalizerThread(ThreadGroup g) { 222 super(g, "Finalizer"); 223 } 224 public void run() { 225 // in case of recursive call to run() 226 if (running) 227 return; 228 229 // Finalizer thread starts before System.initializeSystemClass 230 // is called. Wait until JavaLangAccess is available 231 while (!VM.isBooted()) { 232 // delay until VM completes initialization 233 try { 234 VM.awaitBooted(); 235 } catch (InterruptedException x) { 236 // ignore and continue 237 } 238 } 239 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 240 running = true; 241 for (;;) { 242 try { 243 Finalizer f = (Finalizer)queue.remove(); 244 f.runFinalizer(jla); 245 } catch (InterruptedException x) { 246 // ignore and continue 247 } 248 } 249 } 250 } 251 252 static { 253 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 254 for (ThreadGroup tgn = tg; 255 tgn != null; 256 tg = tgn, tgn = tg.getParent()); 257 Thread finalizer = new FinalizerThread(tg); 258 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 259 finalizer.setDaemon(true); 260 finalizer.start(); 261 } 262 263 }