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 
  31 
  32 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  33                                                           same package as the Reference
  34                                                           class */
  35 
  36     /* A native method that invokes an arbitrary object's finalize method is
  37        required since the finalize method is protected
  38      */
  39     static native void invokeFinalizeMethod(Object o) throws Throwable;
  40 
  41     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
  42     private static Finalizer unfinalized = null;
  43     private static final Object lock = new Object();
  44 
  45     private Finalizer
  46         next = null,
  47         prev = null;
  48 
  49     private boolean hasBeenFinalized() {
  50         return (next == this);
  51     }
  52 
  53     private void add() {
  54         synchronized (lock) {
  55             if (unfinalized != null) {
  56                 this.next = unfinalized;
  57                 unfinalized.prev = this;
  58             }
  59             unfinalized = this;
  60         }
  61     }
  62 
  63     private void remove() {
  64         synchronized (lock) {
  65             if (unfinalized == this) {
  66                 if (this.next != null) {
  67                     unfinalized = this.next;
  68                 } else {
  69                     unfinalized = this.prev;
  70                 }
  71             }
  72             if (this.next != null) {
  73                 this.next.prev = this.prev;
  74             }
  75             if (this.prev != null) {
  76                 this.prev.next = this.next;
  77             }
  78             this.next = this;   /* Indicates that this has been finalized */
  79             this.prev = this;
  80         }
  81     }
  82 
  83     private Finalizer(Object finalizee) {
  84         super(finalizee, queue);
  85         add();
  86     }
  87 
  88     /* Invoked by VM */
  89     static void register(Object finalizee) {
  90         new Finalizer(finalizee);
  91     }
  92 
  93     private void runFinalizer() {
  94         synchronized (this) {
  95             if (hasBeenFinalized()) return;
  96             remove();
  97         }
  98         try {
  99             Object finalizee = this.get();
 100             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
 101                 invokeFinalizeMethod(finalizee);
 102                 /* Clear stack slot containing this variable, to decrease
 103                    the chances of false retention with a conservative GC */
 104                 finalizee = null;
 105             }
 106         } catch (Throwable x) { }
 107         super.clear();
 108     }
 109 
 110     /* Create a privileged secondary finalizer thread in the system thread
 111        group for the given Runnable, and wait for it to complete.
 112 
 113        This method is used by both runFinalization and runFinalizersOnExit.
 114        The former method invokes all pending finalizers, while the latter
 115        invokes all uninvoked finalizers if on-exit finalization has been
 116        enabled.
 117 
 118        These two methods could have been implemented by offloading their work
 119        to the regular finalizer thread and waiting for that thread to finish.
 120        The advantage of creating a fresh thread, however, is that it insulates
 121        invokers of these methods from a stalled or deadlocked finalizer thread.
 122      */
 123     private static void forkSecondaryFinalizer(final Runnable proc) {
 124         AccessController.doPrivileged(
 125             new PrivilegedAction<Void>() {
 126                 public Void run() {
 127                 ThreadGroup tg = Thread.currentThread().getThreadGroup();
 128                 for (ThreadGroup tgn = tg;
 129                      tgn != null;
 130                      tg = tgn, tgn = tg.getParent());
 131                 Thread sft = new Thread(tg, proc, "Secondary finalizer");
 132                 sft.start();
 133                 try {
 134                     sft.join();
 135                 } catch (InterruptedException x) {
 136                     /* Ignore */
 137                 }
 138                 return null;
 139                 }});
 140     }
 141 
 142     /* Called by Runtime.runFinalization() */
 143     static void runFinalization() {
 144         forkSecondaryFinalizer(new Runnable() {
 145             private volatile boolean running;
 146             public void run() {
 147                 if (running)
 148                     return;
 149                 running = true;
 150                 for (;;) {
 151                     Finalizer f = (Finalizer)queue.poll();
 152                     if (f == null) break;
 153                     f.runFinalizer();
 154                 }
 155             }
 156         });
 157     }
 158 
 159     /* Invoked by java.lang.Shutdown */
 160     static void runAllFinalizers() {
 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 class FinalizerThread extends Thread {
 179         private volatile boolean running;
 180         FinalizerThread(ThreadGroup g) {
 181             super(g, "Finalizer");
 182         }
 183         public void run() {
 184             if (running)
 185                 return;
 186             running = true;
 187             for (;;) {
 188                 try {
 189                     Finalizer f = (Finalizer)queue.remove();
 190                     f.runFinalizer();
 191                 } catch (InterruptedException x) {
 192                     continue;
 193                 }
 194             }
 195         }
 196     }
 197 
 198     static {
 199         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 200         for (ThreadGroup tgn = tg;
 201              tgn != null;
 202              tg = tgn, tgn = tg.getParent());
 203         Thread finalizer = new FinalizerThread(tg);
 204         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 205         finalizer.setDaemon(true);
 206         finalizer.start();
 207     }
 208 
 209 }