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 import java.util.Map;
  36 import java.util.HashMap;
  37 import java.util.Map.Entry;
  38 import java.util.Arrays;
  39 
  40 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  41                                                           same package as the Reference
  42                                                           class */
  43 
  44     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
  45     private static Finalizer unfinalized = null;
  46     private static final Object lock = new Object();
  47 
  48     private Finalizer
  49         next = null,
  50         prev = null;
  51 
  52     private boolean hasBeenFinalized() {
  53         return (next == this);
  54     }
  55 
  56     private void add() {
  57         synchronized (lock) {
  58             if (unfinalized != null) {
  59                 this.next = unfinalized;
  60                 unfinalized.prev = this;
  61             }
  62             unfinalized = this;
  63         }
  64     }
  65 
  66     private void remove() {
  67         synchronized (lock) {
  68             if (unfinalized == this) {
  69                 if (this.next != null) {
  70                     unfinalized = this.next;
  71                 } else {
  72                     unfinalized = this.prev;
  73                 }
  74             }
  75             if (this.next != null) {
  76                 this.next.prev = this.prev;
  77             }
  78             if (this.prev != null) {
  79                 this.prev.next = this.next;
  80             }
  81             this.next = this;   /* Indicates that this has been finalized */
  82             this.prev = this;
  83         }
  84     }
  85 
  86     private Finalizer(Object finalizee) {
  87         super(finalizee, queue);
  88         add();
  89     }
  90 
  91     /* Invoked by VM */
  92     static void register(Object finalizee) {
  93         new Finalizer(finalizee);
  94     }
  95 
  96     private void runFinalizer(JavaLangAccess jla) {
  97         synchronized (this) {
  98             if (hasBeenFinalized()) return;
  99             remove();
 100         }
 101         try {
 102             Object finalizee = this.get();
 103             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
 104                 jla.invokeFinalize(finalizee);
 105 
 106                 /* Clear stack slot containing this variable, to decrease
 107                    the chances of false retention with a conservative GC */
 108                 finalizee = null;
 109             }
 110         } catch (Throwable x) { }
 111         super.clear();
 112     }
 113 
 114     /* Create a privileged secondary finalizer thread in the system thread
 115        group for the given Runnable, and wait for it to complete.
 116 
 117        This method is used by both runFinalization and runFinalizersOnExit.
 118        The former method invokes all pending finalizers, while the latter
 119        invokes all uninvoked finalizers if on-exit finalization has been
 120        enabled.
 121 
 122        These two methods could have been implemented by offloading their work
 123        to the regular finalizer thread and waiting for that thread to finish.
 124        The advantage of creating a fresh thread, however, is that it insulates
 125        invokers of these methods from a stalled or deadlocked finalizer thread.
 126      */
 127     private static void forkSecondaryFinalizer(final Runnable proc) {
 128         AccessController.doPrivileged(
 129             new PrivilegedAction<>() {
 130                 public Void run() {
 131                     ThreadGroup tg = Thread.currentThread().getThreadGroup();
 132                     for (ThreadGroup tgn = tg;
 133                          tgn != null;
 134                          tg = tgn, tgn = tg.getParent());
 135                     Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer");
 136                     sft.start();
 137                     try {
 138                         sft.join();
 139                     } catch (InterruptedException x) {
 140                         Thread.currentThread().interrupt();
 141                     }
 142                     return null;
 143                 }});
 144     }
 145 
 146     /* Called by Runtime.runFinalization() */
 147     static void runFinalization() {
 148         if (!VM.isBooted()) {
 149             return;
 150         }
 151 
 152         forkSecondaryFinalizer(new Runnable() {
 153             private volatile boolean running;
 154             public void run() {
 155                 // in case of recursive call to run()
 156                 if (running)
 157                     return;
 158                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 159                 running = true;
 160                 for (;;) {
 161                     Finalizer f = (Finalizer)queue.poll();
 162                     if (f == null) break;
 163                     f.runFinalizer(jla);
 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             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 215             running = true;
 216             for (;;) {
 217                 try {
 218                     Finalizer f = (Finalizer)queue.remove();
 219                     f.runFinalizer(jla);
 220                 } catch (InterruptedException x) {
 221                     // ignore and continue
 222                 }
 223             }
 224         }
 225     }
 226 
 227     // Support for GC.finalizer_info DCMD
 228 
 229     // Fileds of the class below is accessed directly by VM
 230     // using hardcoded field offsets, so don't change
 231     // class layout.
 232     static class FinalizerHistogramEntry {
 233         private final int count;
 234         private final String name;
 235 
 236         int getCount() { return count; }
 237 
 238         FinalizerHistogramEntry(Map.Entry<String, int[]> e) {
 239             name = e.getKey();
 240             count =  e.getValue()[0];
 241         }
 242     };
 243 
 244     // This method called by VM from FinalizerInfoDCmd and
 245     @SuppressWarnings({"unchecked","rawtypes"})
 246     static Object[] getFinalizerHistogram() {
 247         Map<String, int[]> countMap = new HashMap<>();
 248         queue.forEach(r -> {
 249             Object referent = r.get();
 250             if (referent != null) {
 251                 countMap.computeIfAbsent(
 252                     referent.getClass().getName(), k -> new int[1])[0]++;
 253                 /* Clear stack slot containing this variable, to decrease
 254                    the chances of false retention with a conservative GC */
 255                 referent = null;
 256               }
 257         });
 258 
 259         // Copy HashMap entry set to array of objects accessible from VM
 260         FinalizerHistogramEntry fhe[] = new FinalizerHistogramEntry[countMap.size()];
 261         int i = 0;
 262         for (Map.Entry<String, int[]> e : countMap.entrySet()) {
 263              fhe[i++] = new  FinalizerHistogramEntry(e);
 264         }
 265 
 266         Arrays.sort(fhe, (e1, e2) -> Integer.compare(e2.getCount(), e1.getCount()));
 267         return fhe;
 268     }
 269 
 270 
 271     static {
 272         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 273         for (ThreadGroup tgn = tg;
 274              tgn != null;
 275              tg = tgn, tgn = tg.getParent());
 276         Thread finalizer = new FinalizerThread(tg);
 277         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 278         finalizer.setDaemon(true);
 279         finalizer.start();
 280     }
 281 
 282 }