1 /*
2  * Copyright (c) 1997, 2018, 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.misc.JavaLangAccess;
31 import jdk.internal.misc.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 ReferenceQueue<>();
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     /* Invoked by VM */
65     static void register(Object finalizee) {
66         new Finalizer(finalizee);
67     }
68
69     private void runFinalizer(JavaLangAccess jla) {
70         synchronized (lock) {
71             if (this.next == this)      // already finalized
72                 return;
73             // unlink from unfinalized
74             if (unfinalized == this)
75                 unfinalized = this.next;
76             else
77                 this.prev.next = this.next;
78             if (this.next != null)
79                 this.next.prev = this.prev;
80             this.prev = null;
81             this.next = this;           // mark as finalized
82         }
83
84         try {
85             Object finalizee = this.get();
86             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
87                 jla.invokeFinalize(finalizee);
88
89                 // Clear stack slot containing this variable, to decrease
90                 // the chances of false retention with a conservative GC
91                 finalizee = null;
92             }
93         } catch (Throwable x) { }
94         super.clear();
95     }
96
97     /* Create a privileged secondary finalizer thread in the system thread
98      * group for the given Runnable, and wait for it to complete.
99      *
100      * This method is used by runFinalization.
101      *
102      * It could have been implemented by offloading the work to the
103      * regular finalizer thread and waiting for that thread to finish.
104      * The advantage of creating a fresh thread, however, is that it insulates
105      * invokers of that method from a stalled or deadlocked finalizer thread.
106      */

107     private static void forkSecondaryFinalizer(final Runnable proc) {
108         AccessController.doPrivileged(
109             new PrivilegedAction<>() {
110                 public Void run() {
111                     ThreadGroup tg = Thread.currentThread().getThreadGroup();
112                     for (ThreadGroup tgn = tg;
113                          tgn != null;
114                          tg = tgn, tgn = tg.getParent());
115                     Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false);
116                     sft.start();
117                     try {
118                         sft.join();
119                     } catch (InterruptedException x) {
120                         Thread.currentThread().interrupt();
121                     }
122                     return null;
123                 }});
124     }
125
126     /* Called by Runtime.runFinalization() */
127     static void runFinalization() {
128         if (VM.initLevel() == 0) {
129             return;
130         }
131
132         forkSecondaryFinalizer(new Runnable() {
133             private volatile boolean running;
134             public void run() {
135                 // in case of recursive call to run()
136                 if (running)
137                     return;
138                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
139                 running = true;
140                 for (Finalizer f; (f = (Finalizer)queue.poll()) != null; )
141                     f.runFinalizer(jla);
142             }
143         });
144     }
145
146     private static class FinalizerThread extends Thread {
147         private volatile boolean running;
148         FinalizerThread(ThreadGroup g) {
149             super(g, null"Finalizer", 0, false);
150         }
151         public void run() {
152             // in case of recursive call to run()
153             if (running)
154                 return;
155
156             // Finalizer thread starts before System.initializeSystemClass
157             // is called.  Wait until JavaLangAccess is available
158             while (VM.initLevel() == 0) {
159                 // delay until VM completes initialization
160                 try {
161                     VM.awaitInitLevel(1);
162                 } catch (InterruptedException x) {
163                     // ignore and continue
164                 }
165             }
166             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
167             running = true;
168             for (;;) {
169                 try {
170                     Finalizer f = (Finalizer)queue.remove();
171                     f.runFinalizer(jla);
172                 } catch (InterruptedException x) {
173                     // ignore and continue
174                 }
175             }
176         }
177     }
178
179     static {
180         ThreadGroup tg = Thread.currentThread().getThreadGroup();
181         for (ThreadGroup tgn = tg;
182              tgn != null;
183              tg = tgn, tgn = tg.getParent());
184         Thread finalizer = new FinalizerThread(tg);
185         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
186         finalizer.setDaemon(true);
187         finalizer.start();
188     }
189
190 }
191