1 /* 2 * Copyright (c) 1997, 2022, 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 jdk.internal.access.JavaLangAccess; 31 import jdk.internal.access.SharedSecrets; 32 import jdk.internal.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 NativeReferenceQueue<>(); 39 40 /** Head of doubly linked list of Finalizers awaiting finalization. */ 41 private static Finalizer unfinalized = null; 42 43 /** Lock guarding access to unfinalized list. */ 44 private static final Object lock = new Object(); 45 46 private Finalizer next, prev; 47 48 private Finalizer(Object finalizee) { 49 super(finalizee, queue); 50 // push onto unfinalized 51 synchronized (lock) { 52 if (unfinalized != null) { 53 this.next = unfinalized; 54 unfinalized.prev = this; 55 } 56 unfinalized = this; 57 } 58 } 59 60 static ReferenceQueue<Object> getQueue() { 61 return queue; 62 } 63 64 static final boolean ENABLED = isFinalizationEnabled(); 65 66 private static native boolean isFinalizationEnabled(); 67 68 /* Invoked by VM */ 69 static void register(Object finalizee) { 70 if (ENABLED) { 71 new Finalizer(finalizee); 72 } else { 73 throw new InternalError("unexpected call to Finalizer::register when finalization is disabled"); 74 } 75 } 76 77 private void runFinalizer(JavaLangAccess jla) { 78 synchronized (lock) { 79 if (this.next == this) // already finalized 80 return; 81 // unlink from unfinalized 82 if (unfinalized == this) 83 unfinalized = this.next; 84 else 85 this.prev.next = this.next; 86 if (this.next != null) 87 this.next.prev = this.prev; 88 this.prev = null; 89 this.next = this; // mark as finalized 90 } 91 92 try { 93 Object finalizee = this.get(); 94 assert finalizee != null; 95 if (!(finalizee instanceof java.lang.Enum)) { 96 jla.invokeFinalize(finalizee); 97 reportComplete(finalizee); 98 99 // Clear stack slot containing this variable, to decrease 100 // the chances of false retention with a conservative GC 101 finalizee = null; 102 } 103 } catch (Throwable x) { } 104 super.clear(); 105 } 106 107 private static native void reportComplete(Object finalizee); 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 runFinalization. 113 * 114 * It could have been implemented by offloading the work to the 115 * regular finalizer thread and waiting for that thread to finish. 116 * The advantage of creating a fresh thread, however, is that it insulates 117 * invokers of that method from a stalled or deadlocked finalizer thread. 118 */ 119 @SuppressWarnings("removal") 120 private static void forkSecondaryFinalizer(final Runnable proc) { 121 AccessController.doPrivileged( 122 new PrivilegedAction<>() { 123 public Void run() { 124 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 125 for (ThreadGroup tgn = tg; 126 tgn != null; 127 tg = tgn, tgn = tg.getParent()); 128 Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false); 129 sft.start(); 130 try { 131 sft.join(); 132 } catch (InterruptedException x) { 133 Thread.currentThread().interrupt(); 134 } 135 return null; 136 }}); 137 } 138 139 /* Called by Runtime.runFinalization() */ 140 static void runFinalization() { 141 if (VM.initLevel() == 0 || ! ENABLED) { 142 return; 143 } 144 145 forkSecondaryFinalizer(new Runnable() { 146 private volatile boolean running; 147 public void run() { 148 // in case of recursive call to run() 149 if (running) 150 return; 151 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 152 running = true; 153 for (Finalizer f; (f = (Finalizer)queue.poll()) != null; ) 154 f.runFinalizer(jla); 155 } 156 }); 157 } 158 159 private static class FinalizerThread extends Thread { 160 private volatile boolean running; 161 FinalizerThread(ThreadGroup g) { 162 super(g, null, "Finalizer", 0, false); 163 } 164 public void run() { 165 // in case of recursive call to run() 166 if (running) 167 return; 168 169 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 170 running = true; 171 for (;;) { 172 try { 173 Finalizer f = (Finalizer)queue.remove(); 174 f.runFinalizer(jla); 175 } catch (InterruptedException x) { 176 // ignore and continue 177 } 178 } 179 } 180 } 181 182 /** 183 * Start the Finalizer thread as a daemon thread. 184 */ 185 static void startFinalizerThread(ThreadGroup tg) { 186 if (ENABLED) { 187 Thread finalizer = new FinalizerThread(tg); 188 finalizer.setPriority(Thread.MAX_PRIORITY - 2); 189 finalizer.setDaemon(true); 190 finalizer.start(); 191 } 192 } 193 }