1 /* 2 * Copyright (c) 1997, 2015, 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 static ReferenceQueue<Object> getQueue() { 87 return queue; 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 private static class FinalizerThread extends ManagedLocalsThread { 194 private volatile boolean running; 195 FinalizerThread(ThreadGroup g) { 196 super(g, "Finalizer"); 197 } 198 public void run() { 199 // in case of recursive call to run() 200 if (running) 201 return; 202 203 // Finalizer thread starts before System.initializeSystemClass 204 // is called. Wait until JavaLangAccess is available 205 while (!VM.isBooted()) { 206 // delay until VM completes initialization 207 try { 208 VM.awaitBooted(); 209 } catch (InterruptedException x) { 210 // ignore and continue 211 } 212 } 213 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 214 running = true; 215 for (;;) { 216 try { 217 Finalizer f = (Finalizer)queue.remove(); 218 f.runFinalizer(jla); 219 } catch (InterruptedException x) { 220 // ignore and continue 221 } 222 } 223 } 224 } 225 226 static { 227 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 228 for (ThreadGroup tgn = tg; 229 tgn != null; 230 tg = tgn, tgn = tg.getParent()); 231 Thread finalizer = new FinalizerThread(tg); 232 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 233 finalizer.setDaemon(true); 234 finalizer.start(); 235 } 236 237 }