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