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.VM;
  32 
  33 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  34                                                           same package as the Reference
  35                                                           class */
  36 
  37     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
  38     private static Finalizer unfinalized = null;
  39     private static final Object lock = new Object();
  40     private static JavaLangAccess jla = null;
  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() {
  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         assert VM.isBooted();
 143         forkSecondaryFinalizer(new Runnable() {
 144             private volatile boolean running;
 145             public void run() {
 146                 if (running)
 147                     return;
 148                 running = true;
 149                 for (;;) {
 150                     Finalizer f = (Finalizer)queue.poll();
 151                     if (f == null) break;
 152                     f.runFinalizer();
 153                 }
 154             }
 155         });
 156     }
 157 
 158     /* Invoked by java.lang.Shutdown */
 159     static void runAllFinalizers() {
 160         assert VM.isBooted();
 161         forkSecondaryFinalizer(new Runnable() {
 162             private volatile boolean running;
 163             public void run() {
 164                 if (running)
 165                     return;
 166                 running = true;
 167                 for (;;) {
 168                     Finalizer f;
 169                     synchronized (lock) {
 170                         f = unfinalized;
 171                         if (f == null) break;
 172                         unfinalized = f.next;
 173                     }
 174                     f.runFinalizer();
 175                 }}});
 176     }
 177 
 178     private static void ensureAccessAvailable() {
 179         while (!VM.isBooted()) {
 180             // delay ntil VM completes initialization
 181             try {
 182                 VM.awaitBooted();
 183             } catch (InterruptedException x) {
 184                 continue;
 185             }
 186         }
 187         jla = sun.misc.SharedSecrets.getJavaLangAccess();
 188     }
 189 
 190     private static class FinalizerThread extends Thread {
 191         private volatile boolean running;
 192         FinalizerThread(ThreadGroup g) {
 193             super(g, "Finalizer");
 194         }
 195         public void run() {
 196             if (running)
 197                 return;
 198 
 199             // ensure JavaLangAccess is available
 200             ensureAccessAvailable();
 201             running = true;
 202             for (;;) {
 203                 try {
 204                     Finalizer f = (Finalizer)queue.remove();
 205                     f.runFinalizer();
 206                 } catch (InterruptedException x) {
 207                     continue;
 208                 }
 209             }
 210         }
 211     }
 212 
 213     static {
 214         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 215         for (ThreadGroup tgn = tg;
 216              tgn != null;
 217              tg = tgn, tgn = tg.getParent());
 218         Thread finalizer = new FinalizerThread(tg);
 219         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 220         finalizer.setDaemon(true);
 221         finalizer.start();
 222     }
 223 
 224 }