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