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 sun.misc.JavaLangAccess; 31 import sun.misc.SharedSecrets; 32 import sun.misc.VM; 33 import java.util.TreeMap; 34 import java.util.Set; 35 import java.util.Map; 36 37 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in 38 same package as the Reference 39 class */ 40 41 private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); 42 private static Finalizer unfinalized = null; 43 private static final Object lock = new Object(); 44 45 private Finalizer 46 next = null, 47 prev = null; 48 49 private boolean hasBeenFinalized() { 50 return (next == this); 51 } 52 53 private void add() { 54 synchronized (lock) { 55 if (unfinalized != null) { 56 this.next = unfinalized; 57 unfinalized.prev = this; 58 } 59 unfinalized = this; 60 } 61 } 62 63 private void remove() { 64 synchronized (lock) { 65 if (unfinalized == this) { 66 if (this.next != null) { 67 unfinalized = this.next; 68 } else { 69 unfinalized = this.prev; 70 } 71 } 72 if (this.next != null) { 73 this.next.prev = this.prev; 74 } 75 if (this.prev != null) { 76 this.prev.next = this.next; 77 } 78 this.next = this; /* Indicates that this has been finalized */ 79 this.prev = this; 80 } 81 } 82 83 private Finalizer(Object finalizee) { 84 super(finalizee, queue); 85 add(); 86 } 87 88 /* Invoked by VM */ 89 static void register(Object finalizee) { 90 new Finalizer(finalizee); 91 } 92 93 static String printFinalizationQueue() { 94 Finalizer tmp = unfinalized; 95 Map <String, Integer> countMap = new TreeMap<String, Integer>(); 96 synchronized (lock) { 97 while(tmp != null) { 98 Object referent = tmp.get(); 99 if (referent != null) { 100 Class<?> objClass = referent.getClass(); 101 String className = objClass.getName(); 102 Integer cnt = countMap.get(className); 103 if (cnt == null) { 104 countMap.put(className,1); 105 } 106 else { 107 countMap.put(className,cnt + 1); 108 } 109 } 110 tmp = tmp.next; 111 } 112 } 113 114 StringBuilder sb = new StringBuilder(); 115 Set<String> keys = countMap.keySet(); 116 for (String key : keys) { 117 sb.append("Class: " + key + " count: " + countMap.get(key) + "\n"); 118 } 119 120 return sb.toString(); 121 } 122 123 private void runFinalizer(JavaLangAccess jla) { 124 synchronized (this) { 125 if (hasBeenFinalized()) return; 126 remove(); 127 } 128 try { 129 Object finalizee = this.get(); 130 if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { 131 jla.invokeFinalize(finalizee); 132 133 /* Clear stack slot containing this variable, to decrease 134 the chances of false retention with a conservative GC */ 135 finalizee = null; 136 } 137 } catch (Throwable x) { } 138 super.clear(); 139 } 140 141 /* Create a privileged secondary finalizer thread in the system thread 142 group for the given Runnable, and wait for it to complete. 143 144 This method is used by both runFinalization and runFinalizersOnExit. 145 The former method invokes all pending finalizers, while the latter 146 invokes all uninvoked finalizers if on-exit finalization has been 147 enabled. 148 149 These two methods could have been implemented by offloading their work 150 to the regular finalizer thread and waiting for that thread to finish. 151 The advantage of creating a fresh thread, however, is that it insulates 152 invokers of these methods from a stalled or deadlocked finalizer thread. 153 */ 154 private static void forkSecondaryFinalizer(final Runnable proc) { 155 AccessController.doPrivileged( 156 new PrivilegedAction<Void>() { 157 public Void run() { 158 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 159 for (ThreadGroup tgn = tg; 160 tgn != null; 161 tg = tgn, tgn = tg.getParent()); 162 Thread sft = new Thread(tg, proc, "Secondary finalizer"); 163 sft.start(); 164 try { 165 sft.join(); 166 } catch (InterruptedException x) { 167 Thread.currentThread().interrupt(); 168 } 169 return null; 170 }}); 171 } 172 173 /* Called by Runtime.runFinalization() */ 174 static void runFinalization() { 175 if (!VM.isBooted()) { 176 return; 177 } 178 179 forkSecondaryFinalizer(new Runnable() { 180 private volatile boolean running; 181 public void run() { 182 // in case of recursive call to run() 183 if (running) 184 return; 185 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 186 running = true; 187 for (;;) { 188 Finalizer f = (Finalizer)queue.poll(); 189 if (f == null) break; 190 f.runFinalizer(jla); 191 } 192 } 193 }); 194 } 195 196 /* Invoked by java.lang.Shutdown */ 197 static void runAllFinalizers() { 198 if (!VM.isBooted()) { 199 return; 200 } 201 202 forkSecondaryFinalizer(new Runnable() { 203 private volatile boolean running; 204 public void run() { 205 // in case of recursive call to run() 206 if (running) 207 return; 208 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 209 running = true; 210 for (;;) { 211 Finalizer f; 212 synchronized (lock) { 213 f = unfinalized; 214 if (f == null) break; 215 unfinalized = f.next; 216 } 217 f.runFinalizer(jla); 218 }}}); 219 } 220 221 private static class FinalizerThread extends Thread { 222 private volatile boolean running; 223 FinalizerThread(ThreadGroup g) { 224 super(g, "Finalizer"); 225 } 226 public void run() { 227 // in case of recursive call to run() 228 if (running) 229 return; 230 231 // Finalizer thread starts before System.initializeSystemClass 232 // is called. Wait until JavaLangAccess is available 233 while (!VM.isBooted()) { 234 // delay until VM completes initialization 235 try { 236 VM.awaitBooted(); 237 } catch (InterruptedException x) { 238 // ignore and continue 239 } 240 } 241 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 242 running = true; 243 for (;;) { 244 try { 245 Finalizer f = (Finalizer)queue.remove(); 246 f.runFinalizer(jla); 247 } catch (InterruptedException x) { 248 // ignore and continue 249 } 250 } 251 } 252 } 253 254 static { 255 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 256 for (ThreadGroup tgn = tg; 257 tgn != null; 258 tg = tgn, tgn = tg.getParent()); 259 Thread finalizer = new FinalizerThread(tg); 260 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 261 finalizer.setDaemon(true); 262 finalizer.start(); 263 } 264 265 }