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 } 201 }); 202 @SuppressWarnings("unchecked") 203 Map.Entry<String, int[]>[] entries = countMap.entrySet().toArray( 204 new Map.Entry[countMap.size()] 205 ); 206 Arrays.sort(entries, (e1, e2) -> 207 Integer.compare(e2.getValue()[0], e1.getValue()[0])); 208 StringBuilder sb = new StringBuilder(); 209 for (Map.Entry<String, int[]> e : entries) { 210 sb.append("Class: ").append(e.getKey()) 211 .append(" count: ").append(e.getValue()[0]).append("\n"); 212 } 213 return sb.toString(); 214 } 215 216 private static class FinalizerThread extends ManagedLocalsThread { 217 private volatile boolean running; 218 FinalizerThread(ThreadGroup g) { 219 super(g, "Finalizer"); 220 } 221 public void run() { 222 // in case of recursive call to run() 223 if (running) 224 return; 225 226 // Finalizer thread starts before System.initializeSystemClass 227 // is called. Wait until JavaLangAccess is available 228 while (!VM.isBooted()) { 229 // delay until VM completes initialization 230 try { 231 VM.awaitBooted(); 232 } catch (InterruptedException x) { 233 // ignore and continue 234 } 235 } 236 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 237 running = true; 238 for (;;) { 239 try { 240 Finalizer f = (Finalizer)queue.remove(); 241 f.runFinalizer(jla); 242 } catch (InterruptedException x) { 243 // ignore and continue 244 } 245 } 246 } 247 } 248 249 static { 250 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 251 for (ThreadGroup tgn = tg; 252 tgn != null; 253 tg = tgn, tgn = tg.getParent()); 254 Thread finalizer = new FinalizerThread(tg); 255 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 256 finalizer.setDaemon(true); 257 finalizer.start(); 258 } 259 260 }