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 java.util.Arrays;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 
  34 import sun.misc.JavaLangAccess;
  35 import sun.misc.ManagedLocalsThread;
  36 import sun.misc.SharedSecrets;
  37 import sun.misc.VM;
  38 
  39 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  40                                                           same package as the Reference
  41                                                           class */
  42 
  43     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
  44     private static Finalizer unfinalized = null;
  45     private static final Object lock = new Object();
  46 
  47     private Finalizer
  48         next = null,
  49         prev = null;
  50 
  51     private boolean hasBeenFinalized() {
  52         return (next == this);
  53     }
  54 
  55     private void add() {
  56         synchronized (lock) {
  57             if (unfinalized != null) {
  58                 this.next = unfinalized;
  59                 unfinalized.prev = this;
  60             }
  61             unfinalized = this;
  62         }
  63     }
  64 
  65     private void remove() {
  66         synchronized (lock) {
  67             if (unfinalized == this) {
  68                 if (this.next != null) {
  69                     unfinalized = this.next;
  70                 } else {
  71                     unfinalized = this.prev;
  72                 }
  73             }
  74             if (this.next != null) {
  75                 this.next.prev = this.prev;
  76             }
  77             if (this.prev != null) {
  78                 this.prev.next = this.next;
  79             }
  80             this.next = this;   /* Indicates that this has been finalized */
  81             this.prev = this;
  82         }
  83     }
  84 
  85     private Finalizer(Object finalizee) {
  86         super(finalizee, queue);
  87         add();
  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     static String printFinalizationQueue() {
 194         Map<String, int[]> countMap = new HashMap<>();
 195         queue.forEach(r -> {
 196             Object referent = r.get();
 197             if (referent != null) {
 198                 countMap.computeIfAbsent(
 199                     referent.getClass().getName(), k -> new int[1])[0]++;
 200             }
 201         });
 202         @SuppressWarnings("unchecked")
 203         Map.Entry<String, int[]>[] entries = countMap.entrySet().toArray(
 204             new Map.Entry[countMap.size()]
 205         );
 206         Arrays.sort(entries, (e1, e2) ->
 207             Integer.compare(e2.getValue()[0], e1.getValue()[0]));
 208         StringBuilder sb = new StringBuilder();
 209         for (Map.Entry<String, int[]> e : entries) {
 210             sb.append("Class: ").append(e.getKey())
 211               .append(" count: ").append(e.getValue()[0]).append("\n");
 212         }
 213         return sb.toString();
 214     }
 215 
 216     private static class FinalizerThread extends ManagedLocalsThread {
 217         private volatile boolean running;
 218         FinalizerThread(ThreadGroup g) {
 219             super(g, "Finalizer");
 220         }
 221         public void run() {
 222             // in case of recursive call to run()
 223             if (running)
 224                 return;
 225 
 226             // Finalizer thread starts before System.initializeSystemClass
 227             // is called.  Wait until JavaLangAccess is available
 228             while (!VM.isBooted()) {
 229                 // delay until VM completes initialization
 230                 try {
 231                     VM.awaitBooted();
 232                 } catch (InterruptedException x) {
 233                     // ignore and continue
 234                 }
 235             }
 236             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 237             running = true;
 238             for (;;) {
 239                 try {
 240                     Finalizer f = (Finalizer)queue.remove();
 241                     f.runFinalizer(jla);
 242                 } catch (InterruptedException x) {
 243                     // ignore and continue
 244                 }
 245             }
 246         }
 247     }
 248 
 249     static {
 250         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 251         for (ThreadGroup tgn = tg;
 252              tgn != null;
 253              tg = tgn, tgn = tg.getParent());
 254         Thread finalizer = new FinalizerThread(tg);
 255         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 256         finalizer.setDaemon(true);
 257         finalizer.start();
 258     }
 259 
 260 }