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                         Thread.currentThread().interrupt();
 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         forkSecondaryFinalizer(new Runnable() {
 147             private volatile boolean running;
 148             public void run() {
 149                 // in case of recursive call to run()
 150                 if (running)
 151                     return;
 152                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 153                 running = true;
 154                 for (;;) {
 155                     Finalizer f = (Finalizer)queue.poll();
 156                     if (f == null) break;
 157                     f.runFinalizer(jla);
 158                 }
 159             }
 160         });
 161     }
 162 
 163     /* Invoked by java.lang.Shutdown */
 164     static void runAllFinalizers() {
 165         if (!VM.isBooted()) {
 166             return;
 167         }
 168 
 169         forkSecondaryFinalizer(new Runnable() {
 170             private volatile boolean running;
 171             public void run() {
 172                 // in case of recursive call to run()
 173                 if (running)
 174                     return;
 175                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 176                 running = true;
 177                 for (;;) {
 178                     Finalizer f;
 179                     synchronized (lock) {
 180                         f = unfinalized;
 181                         if (f == null) break;
 182                         unfinalized = f.next;
 183                     }
 184                     f.runFinalizer(jla);
 185                 }}});
 186     }
 187 
 188     private static class FinalizerThread extends Thread {
 189         private volatile boolean running;
 190         FinalizerThread(ThreadGroup g) {
 191             super(g, "Finalizer");
 192         }
 193         public void run() {
 194             // in case of recursive call to run()
 195             if (running)
 196                 return;
 197 
 198             // Finalizer thread starts before System.initializeSystemClass
 199             // is called.  Wait until JavaLangAccess is available
 200             while (!VM.isBooted()) {
 201                 // delay until VM completes initialization
 202                 try {
 203                     VM.awaitBooted();
 204                 } catch (InterruptedException x) {
 205                     // ignore and continue
 206                 }
 207             }
 208             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 209             running = true;
 210             for (;;) {
 211                 try {
 212                     Finalizer f = (Finalizer)queue.remove();
 213                     f.runFinalizer(jla);
 214                 } catch (InterruptedException x) {
 215                     // ignore and continue
 216                 }
 217             }
 218         }
 219     }
 220 
 221     static {
 222         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 223         for (ThreadGroup tgn = tg;
 224              tgn != null;
 225              tg = tgn, tgn = tg.getParent());
 226         Thread finalizer = new FinalizerThread(tg);
 227         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 228         finalizer.setDaemon(true);
 229         finalizer.start();
 230     }
 231 
 232 }