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 sun.misc.JavaLangAccess;
  31 import sun.misc.SharedSecrets;
  32 import sun.misc.VM;
  33 import java.util.TreeMap;
  34 import java.util.Set;
  35 import java.util.Map;
  36 
  37 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  38                                                           same package as the Reference
  39                                                           class */
  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     static String printFinalizationQueue() {
  94         Finalizer tmp = unfinalized;
  95         Map <String, Integer> countMap = new TreeMap<String, Integer>();
  96         synchronized (lock) {
  97            while(tmp != null) {
  98               Object referent = tmp.get();
  99               if (referent != null) {
 100                   Class<?> objClass = referent.getClass();
 101                   String className = objClass.getName();
 102                   Integer cnt = countMap.get(className);
 103                   if (cnt == null) {
 104                       countMap.put(className,1);
 105                   }
 106                   else {
 107                       countMap.put(className,cnt + 1);
 108                   }
 109               }
 110               tmp = tmp.next;
 111            }
 112         }
 113 
 114         StringBuilder sb = new StringBuilder();
 115         Set<String> keys = countMap.keySet();
 116         for (String key : keys) {
 117             sb.append("Class: " + key + " count: "  + countMap.get(key) + "\n");
 118         }
 119 
 120         return sb.toString();
 121     }
 122 
 123     private void runFinalizer(JavaLangAccess jla) {
 124         synchronized (this) {
 125             if (hasBeenFinalized()) return;
 126             remove();
 127         }
 128         try {
 129             Object finalizee = this.get();
 130             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
 131                 jla.invokeFinalize(finalizee);
 132 
 133                 /* Clear stack slot containing this variable, to decrease
 134                    the chances of false retention with a conservative GC */
 135                 finalizee = null;
 136             }
 137         } catch (Throwable x) { }
 138         super.clear();
 139     }
 140 
 141     /* Create a privileged secondary finalizer thread in the system thread
 142        group for the given Runnable, and wait for it to complete.
 143 
 144        This method is used by both runFinalization and runFinalizersOnExit.
 145        The former method invokes all pending finalizers, while the latter
 146        invokes all uninvoked finalizers if on-exit finalization has been
 147        enabled.
 148 
 149        These two methods could have been implemented by offloading their work
 150        to the regular finalizer thread and waiting for that thread to finish.
 151        The advantage of creating a fresh thread, however, is that it insulates
 152        invokers of these methods from a stalled or deadlocked finalizer thread.
 153      */
 154     private static void forkSecondaryFinalizer(final Runnable proc) {
 155         AccessController.doPrivileged(
 156             new PrivilegedAction<Void>() {
 157                 public Void run() {
 158                     ThreadGroup tg = Thread.currentThread().getThreadGroup();
 159                     for (ThreadGroup tgn = tg;
 160                          tgn != null;
 161                          tg = tgn, tgn = tg.getParent());
 162                     Thread sft = new Thread(tg, proc, "Secondary finalizer");
 163                     sft.start();
 164                     try {
 165                         sft.join();
 166                     } catch (InterruptedException x) {
 167                         Thread.currentThread().interrupt();
 168                     }
 169                     return null;
 170                 }});
 171     }
 172 
 173     /* Called by Runtime.runFinalization() */
 174     static void runFinalization() {
 175         if (!VM.isBooted()) {
 176             return;
 177         }
 178 
 179         forkSecondaryFinalizer(new Runnable() {
 180             private volatile boolean running;
 181             public void run() {
 182                 // in case of recursive call to run()
 183                 if (running)
 184                     return;
 185                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 186                 running = true;
 187                 for (;;) {
 188                     Finalizer f = (Finalizer)queue.poll();
 189                     if (f == null) break;
 190                     f.runFinalizer(jla);
 191                 }
 192             }
 193         });
 194     }
 195 
 196     /* Invoked by java.lang.Shutdown */
 197     static void runAllFinalizers() {
 198         if (!VM.isBooted()) {
 199             return;
 200         }
 201 
 202         forkSecondaryFinalizer(new Runnable() {
 203             private volatile boolean running;
 204             public void run() {
 205                 // in case of recursive call to run()
 206                 if (running)
 207                     return;
 208                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 209                 running = true;
 210                 for (;;) {
 211                     Finalizer f;
 212                     synchronized (lock) {
 213                         f = unfinalized;
 214                         if (f == null) break;
 215                         unfinalized = f.next;
 216                     }
 217                     f.runFinalizer(jla);
 218                 }}});
 219     }
 220 
 221     private static class FinalizerThread extends Thread {
 222         private volatile boolean running;
 223         FinalizerThread(ThreadGroup g) {
 224             super(g, "Finalizer");
 225         }
 226         public void run() {
 227             // in case of recursive call to run()
 228             if (running)
 229                 return;
 230 
 231             // Finalizer thread starts before System.initializeSystemClass
 232             // is called.  Wait until JavaLangAccess is available
 233             while (!VM.isBooted()) {
 234                 // delay until VM completes initialization
 235                 try {
 236                     VM.awaitBooted();
 237                 } catch (InterruptedException x) {
 238                     // ignore and continue
 239                 }
 240             }
 241             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 242             running = true;
 243             for (;;) {
 244                 try {
 245                     Finalizer f = (Finalizer)queue.remove();
 246                     f.runFinalizer(jla);
 247                 } catch (InterruptedException x) {
 248                     // ignore and continue
 249                 }
 250             }
 251         }
 252     }
 253 
 254     static {
 255         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 256         for (ThreadGroup tgn = tg;
 257              tgn != null;
 258              tg = tgn, tgn = tg.getParent());
 259         Thread finalizer = new FinalizerThread(tg);
 260         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 261         finalizer.setDaemon(true);
 262         finalizer.start();
 263     }
 264 
 265 }