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                 /* Clear stack slot containing this variable, to decrease
 201                    the chances of false retention with a conservative GC */
 202                 referent = null;
 203             }
 204         });
 205         @SuppressWarnings("unchecked")
 206         Map.Entry<String, int[]>[] entries = countMap.entrySet().toArray(
 207             new Map.Entry[countMap.size()]
 208         );
 209         Arrays.sort(entries, (e1, e2) ->
 210             Integer.compare(e2.getValue()[0], e1.getValue()[0]));
 211         StringBuilder sb = new StringBuilder();
 212         for (Map.Entry<String, int[]> e : entries) {
 213             sb.append("Class: ").append(e.getKey())
 214               .append(" count: ").append(e.getValue()[0]).append("\n");
 215         }
 216         return sb.toString();
 217     }
 218 
 219     private static class FinalizerThread extends ManagedLocalsThread {
 220         private volatile boolean running;
 221         FinalizerThread(ThreadGroup g) {
 222             super(g, "Finalizer");
 223         }
 224         public void run() {
 225             // in case of recursive call to run()
 226             if (running)
 227                 return;
 228 
 229             // Finalizer thread starts before System.initializeSystemClass
 230             // is called.  Wait until JavaLangAccess is available
 231             while (!VM.isBooted()) {
 232                 // delay until VM completes initialization
 233                 try {
 234                     VM.awaitBooted();
 235                 } catch (InterruptedException x) {
 236                     // ignore and continue
 237                 }
 238             }
 239             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 240             running = true;
 241             for (;;) {
 242                 try {
 243                     Finalizer f = (Finalizer)queue.remove();
 244                     f.runFinalizer(jla);
 245                 } catch (InterruptedException x) {
 246                     // ignore and continue
 247                 }
 248             }
 249         }
 250     }
 251 
 252     static {
 253         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 254         for (ThreadGroup tgn = tg;
 255              tgn != null;
 256              tg = tgn, tgn = tg.getParent());
 257         Thread finalizer = new FinalizerThread(tg);
 258         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 259         finalizer.setDaemon(true);
 260         finalizer.start();
 261     }
 262 
 263 }