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 import java.util.Map; 36 import java.util.HashMap; 37 import java.util.Map.Entry; 38 import java.util.Arrays; 39 40 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in 41 same package as the Reference 42 class */ 43 44 private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); 45 private static Finalizer unfinalized = null; 46 private static final Object lock = new Object(); 47 48 private Finalizer 49 next = null, 50 prev = null; 51 52 private boolean hasBeenFinalized() { 53 return (next == this); 54 } 55 56 private void add() { 57 synchronized (lock) { 58 if (unfinalized != null) { 59 this.next = unfinalized; 60 unfinalized.prev = this; 61 } 62 unfinalized = this; 63 } 64 } 65 66 private void remove() { 67 synchronized (lock) { 68 if (unfinalized == this) { 69 if (this.next != null) { 70 unfinalized = this.next; 71 } else { 72 unfinalized = this.prev; 73 } 74 } 75 if (this.next != null) { 76 this.next.prev = this.prev; 77 } 78 if (this.prev != null) { 79 this.prev.next = this.next; 80 } 81 this.next = this; /* Indicates that this has been finalized */ 82 this.prev = this; 83 } 84 } 85 86 private Finalizer(Object finalizee) { 87 super(finalizee, queue); 88 add(); 89 } 90 91 /* Invoked by VM */ 92 static void register(Object finalizee) { 93 new Finalizer(finalizee); 94 } 95 96 private void runFinalizer(JavaLangAccess jla) { 97 synchronized (this) { 98 if (hasBeenFinalized()) return; 99 remove(); 100 } 101 try { 102 Object finalizee = this.get(); 103 if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { 104 jla.invokeFinalize(finalizee); 105 106 /* Clear stack slot containing this variable, to decrease 107 the chances of false retention with a conservative GC */ 108 finalizee = null; 109 } 110 } catch (Throwable x) { } 111 super.clear(); 112 } 113 114 /* Create a privileged secondary finalizer thread in the system thread 115 group for the given Runnable, and wait for it to complete. 116 117 This method is used by both runFinalization and runFinalizersOnExit. 118 The former method invokes all pending finalizers, while the latter 119 invokes all uninvoked finalizers if on-exit finalization has been 120 enabled. 121 122 These two methods could have been implemented by offloading their work 123 to the regular finalizer thread and waiting for that thread to finish. 124 The advantage of creating a fresh thread, however, is that it insulates 125 invokers of these methods from a stalled or deadlocked finalizer thread. 126 */ 127 private static void forkSecondaryFinalizer(final Runnable proc) { 128 AccessController.doPrivileged( 129 new PrivilegedAction<>() { 130 public Void run() { 131 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 132 for (ThreadGroup tgn = tg; 133 tgn != null; 134 tg = tgn, tgn = tg.getParent()); 135 Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer"); 136 sft.start(); 137 try { 138 sft.join(); 139 } catch (InterruptedException x) { 140 Thread.currentThread().interrupt(); 141 } 142 return null; 143 }}); 144 } 145 146 /* Called by Runtime.runFinalization() */ 147 static void runFinalization() { 148 if (!VM.isBooted()) { 149 return; 150 } 151 152 forkSecondaryFinalizer(new Runnable() { 153 private volatile boolean running; 154 public void run() { 155 // in case of recursive call to run() 156 if (running) 157 return; 158 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 159 running = true; 160 for (;;) { 161 Finalizer f = (Finalizer)queue.poll(); 162 if (f == null) break; 163 f.runFinalizer(jla); 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 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 215 running = true; 216 for (;;) { 217 try { 218 Finalizer f = (Finalizer)queue.remove(); 219 f.runFinalizer(jla); 220 } catch (InterruptedException x) { 221 // ignore and continue 222 } 223 } 224 } 225 } 226 227 // Support for GC.finalizer_info DCMD 228 229 // Fileds of the class below is accessed directly by VM 230 // using hardcoded field offsets, so don't change 231 // class layout. 232 static class FinalizerHistogramEntry { 233 private final int count; 234 private final String name; 235 236 int getCount() { return count; } 237 238 FinalizerHistogramEntry(Map.Entry<String, int[]> e) { 239 name = e.getKey(); 240 count = e.getValue()[0]; 241 } 242 }; 243 244 // This method called by VM from FinalizerInfoDCmd and 245 @SuppressWarnings({"unchecked","rawtypes"}) 246 static Object[] getFinalizerHistogram() { 247 Map<String, int[]> countMap = new HashMap<>(); 248 queue.forEach(r -> { 249 Object referent = r.get(); 250 if (referent != null) { 251 countMap.computeIfAbsent( 252 referent.getClass().getName(), k -> new int[1])[0]++; 253 /* Clear stack slot containing this variable, to decrease 254 the chances of false retention with a conservative GC */ 255 referent = null; 256 } 257 }); 258 259 // Copy HashMap entry set to array of objects accessible from VM 260 FinalizerHistogramEntry fhe[] = new FinalizerHistogramEntry[countMap.size()]; 261 int i = 0; 262 for (Map.Entry<String, int[]> e : countMap.entrySet()) { 263 fhe[i++] = new FinalizerHistogramEntry(e); 264 } 265 266 Arrays.sort(fhe, (e1, e2) -> Integer.compare(e2.getCount(), e1.getCount())); 267 return fhe; 268 } 269 270 271 static { 272 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 273 for (ThreadGroup tgn = tg; 274 tgn != null; 275 tg = tgn, tgn = tg.getParent()); 276 Thread finalizer = new FinalizerThread(tg); 277 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 278 finalizer.setDaemon(true); 279 finalizer.start(); 280 } 281 282 }