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 implements Finalizable { /* same package as the Reference */ 37 /* class */ 38 39 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 /** Finalizable implementation */ 110 @Override 111 public void runFinalizable() { 112 runFinalizer(SharedSecrets.getJavaLangAccess()); 113 } 114 115 /* Create a privileged secondary finalizer thread in the system thread 116 group for the given Runnable, and wait for it to complete. 117 118 This method is used by both runFinalization and runFinalizersOnExit. 119 The former method invokes all pending finalizers, while the latter 120 invokes all uninvoked finalizers if on-exit finalization has been 121 enabled. 122 123 These two methods could have been implemented by offloading their work 124 to the regular finalizer thread and waiting for that thread to finish. 125 The advantage of creating a fresh thread, however, is that it insulates 126 invokers of these methods from a stalled or deadlocked finalizer thread. 127 */ 128 private static void forkSecondaryFinalizer(final Runnable proc) { 129 AccessController.doPrivileged( 130 new PrivilegedAction<>() { 131 public Void run() { 132 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 133 for (ThreadGroup tgn = tg; 134 tgn != null; 135 tg = tgn, tgn = tg.getParent()); 136 Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer"); 137 sft.start(); 138 try { 139 sft.join(); 140 } catch (InterruptedException x) { 141 Thread.currentThread().interrupt(); 142 } 143 return null; 144 }}); 145 } 146 147 /* Called by Runtime.runFinalization() */ 148 static void runFinalization() { 149 if (!VM.isBooted()) { 150 return; 151 } 152 153 forkSecondaryFinalizer(new Runnable() { 154 private volatile boolean running; 155 public void run() { 156 // in case of recursive call to run() 157 if (running) 158 return; 159 running = true; 160 for (;;) { 161 Finalizable f = (Finalizable)queue.poll(); 162 if (f == null) break; 163 f.runFinalizable(); 164 } 165 } 166 }); 167 } 168 169 /* Invoked by java.lang.Shutdown */ 170 static void runAllFinalizers() { 171 if (!VM.isBooted()) { 172 return; 173 } 174 175 forkSecondaryFinalizer(new Runnable() { 176 private volatile boolean running; 177 public void run() { 178 // in case of recursive call to run() 179 if (running) 180 return; 181 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 182 running = true; 183 for (;;) { 184 Finalizer f; 185 synchronized (lock) { 186 f = unfinalized; 187 if (f == null) break; 188 unfinalized = f.next; 189 } 190 f.runFinalizer(jla); 191 }}}); 192 } 193 194 private static class FinalizerThread extends ManagedLocalsThread { 195 private volatile boolean running; 196 FinalizerThread(ThreadGroup g) { 197 super(g, "Finalizer"); 198 } 199 public void run() { 200 // in case of recursive call to run() 201 if (running) 202 return; 203 204 // Finalizer thread starts before System.initializeSystemClass 205 // is called. Wait until JavaLangAccess is available 206 while (!VM.isBooted()) { 207 // delay until VM completes initialization 208 try { 209 VM.awaitBooted(); 210 } catch (InterruptedException x) { 211 // ignore and continue 212 } 213 } 214 running = true; 215 for (;;) { 216 try { 217 Finalizable f = (Finalizable)queue.remove(); 218 f.runFinalizable(); 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 }