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