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.SharedSecrets;
  32 import sun.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     private static Finalizer unfinalized = null;
  40     private static final Object lock = new Object();
  41 
  42     private Finalizer
  43         next = null,
  44         prev = null;
  45 
  46     private boolean hasBeenFinalized() {
  47         return (next == this);
  48     }
  49 
  50     private void add() {
  51         synchronized (lock) {
  52             if (unfinalized != null) {
  53                 this.next = unfinalized;
  54                 unfinalized.prev = this;
  55             }
  56             unfinalized = this;
  57         }
  58     }
  59 
  60     private void remove() {
  61         synchronized (lock) {
  62             if (unfinalized == this) {
  63                 if (this.next != null) {
  64                     unfinalized = this.next;
  65                 } else {
  66                     unfinalized = this.prev;
  67                 }
  68             }
  69             if (this.next != null) {
  70                 this.next.prev = this.prev;
  71             }
  72             if (this.prev != null) {
  73                 this.prev.next = this.next;
  74             }
  75             this.next = this;   /* Indicates that this has been finalized */
  76             this.prev = this;
  77         }
  78     }
  79 
  80     private Finalizer(Object finalizee) {
  81         super(finalizee, queue);
  82         add();
  83     }
  84 
  85     /* Invoked by VM */
  86     static void register(Object finalizee) {
  87         new Finalizer(finalizee);
  88     }
  89 
  90     private void runFinalizer(JavaLangAccess jla) {
  91         synchronized (this) {
  92             if (hasBeenFinalized()) return;
  93             remove();
  94         }
  95         try {
  96             Object finalizee = this.get();
  97             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
  98                 jla.invokeFinalize(finalizee);
  99 
 100                 /* Clear stack slot containing this variable, to decrease
 101                    the chances of false retention with a conservative GC */
 102                 finalizee = null;
 103             }
 104         } catch (Throwable x) { }
 105         super.clear();
 106     }
 107 
 108     /* Create a privileged secondary finalizer thread in the system thread
 109        group for the given Runnable, and wait for it to complete.
 110 
 111        This method is used by both runFinalization and runFinalizersOnExit.
 112        The former method invokes all pending finalizers, while the latter
 113        invokes all uninvoked finalizers if on-exit finalization has been
 114        enabled.
 115 
 116        These two methods could have been implemented by offloading their work
 117        to the regular finalizer thread and waiting for that thread to finish.
 118        The advantage of creating a fresh thread, however, is that it insulates
 119        invokers of these methods from a stalled or deadlocked finalizer thread.
 120      */
 121     private static void forkSecondaryFinalizer(final Runnable proc) {
 122         AccessController.doPrivileged(
 123             new PrivilegedAction<Void>() {
 124                 public Void run() {
 125                 ThreadGroup tg = Thread.currentThread().getThreadGroup();
 126                 for (ThreadGroup tgn = tg;
 127                      tgn != null;
 128                      tg = tgn, tgn = tg.getParent());
 129                 Thread sft = new Thread(tg, proc, "Secondary finalizer");
 130                 sft.start();
 131                 try {
 132                     sft.join();
 133                 } catch (InterruptedException x) {
 134                     /* Ignore */
 135                 }
 136                 return null;
 137                 }});
 138     }
 139 
 140     /* Called by Runtime.runFinalization() */
 141     static void runFinalization() {
 142         if (!VM.isBooted()) {
 143             return;
 144         }
 145         
 146         final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 147         forkSecondaryFinalizer(new Runnable() {
 148             private volatile boolean running;
 149             public void run() {
 150                 if (running)
 151                     return;
 152                 running = true;
 153                 for (;;) {
 154                     Finalizer f = (Finalizer)queue.poll();
 155                     if (f == null) break;
 156                     f.runFinalizer(jla);
 157                 }
 158             }
 159         });
 160     }
 161 
 162     /* Invoked by java.lang.Shutdown */
 163     static void runAllFinalizers() {
 164         if (!VM.isBooted()) {
 165             return;
 166         }
 167 
 168         final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 169         forkSecondaryFinalizer(new Runnable() {
 170             private volatile boolean running;
 171             public void run() {
 172                 if (running)
 173                     return;
 174                 running = true;
 175                 for (;;) {
 176                     Finalizer f;
 177                     synchronized (lock) {
 178                         f = unfinalized;
 179                         if (f == null) break;
 180                         unfinalized = f.next;
 181                     }
 182                     f.runFinalizer(jla);
 183                 }}});
 184     }
 185 
 186     private static class FinalizerThread extends Thread {
 187         private volatile boolean running;
 188         FinalizerThread(ThreadGroup g) {
 189             super(g, "Finalizer");
 190         }
 191         public void run() {
 192             if (running)
 193                 return;
 194 
 195             // Finalizer thread starts before System.initializeSystemClass 
 196             // is called.  Wait until JavaLangAccess is available
 197             while (!VM.isBooted()) {
 198                 // delay until VM completes initialization
 199                 try {
 200                     VM.awaitBooted();
 201                 } catch (InterruptedException x) {
 202                     // ignore and continue
 203                 }
 204             }
 205             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 206             running = true;
 207             for (;;) {
 208                 try {
 209                     Finalizer f = (Finalizer)queue.remove();
 210                     f.runFinalizer(jla);
 211                 } catch (InterruptedException x) {
 212                     // ignore and continue
 213                 }
 214             }
 215         }
 216     }
 217 
 218     static {
 219         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 220         for (ThreadGroup tgn = tg;
 221              tgn != null;
 222              tg = tgn, tgn = tg.getParent());
 223         Thread finalizer = new FinalizerThread(tg);
 224         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 225         finalizer.setDaemon(true);
 226         finalizer.start();
 227     }
 228 
 229 }