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