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