< prev index next >

src/java.base/share/classes/java/lang/ref/Finalizer.java

Print this page
8197812: (ref) Data race in Finalizer
Reviewed-by: plevart, mchung


  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 jdk.internal.misc.JavaLangAccess;
  31 import jdk.internal.misc.SharedSecrets;
  32 import jdk.internal.misc.VM;
  33 
  34 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  35                                                           same package as the Reference
  36                                                           class */
  37 
  38     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();


  39     private static Finalizer unfinalized = null;


  40     private static final Object lock = new Object();
  41 
  42     private Finalizer
  43         next = null,
  44         prev = null;
  45 
  46     private boolean hasBeenFinalized() {
  47         return (next == this);
  48     }
  49 
  50     private void add() {
  51         synchronized (lock) {
  52             if (unfinalized != null) {
  53                 this.next = unfinalized;
  54                 unfinalized.prev = this;
  55             }
  56             unfinalized = this;
  57         }
  58     }
  59 
  60     private void remove() {
  61         synchronized (lock) {
  62             if (unfinalized == this) {
  63                 if (this.next != null) {
  64                     unfinalized = this.next;
  65                 } else {
  66                     unfinalized = this.prev;
  67                 }
  68             }
  69             if (this.next != null) {
  70                 this.next.prev = this.prev;
  71             }
  72             if (this.prev != null) {
  73                 this.prev.next = this.next;
  74             }
  75             this.next = this;   /* Indicates that this has been finalized */
  76             this.prev = this;
  77         }
  78     }
  79 
  80     private Finalizer(Object finalizee) {
  81         super(finalizee, queue);
  82         add();
  83     }
  84 
  85     static ReferenceQueue<Object> getQueue() {
  86         return queue;
  87     }
  88 
  89     /* Invoked by VM */
  90     static void register(Object finalizee) {
  91         new Finalizer(finalizee);
  92     }
  93 
  94     private void runFinalizer(JavaLangAccess jla) {
  95         synchronized (this) {
  96             if (hasBeenFinalized()) return;
  97             remove();









  98         }




  99         try {
 100             Object finalizee = this.get();
 101             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
 102                 jla.invokeFinalize(finalizee);
 103 
 104                 /* Clear stack slot containing this variable, to decrease
 105                    the chances of false retention with a conservative GC */
 106                 finalizee = null;
 107             }
 108         } catch (Throwable x) { }
 109         super.clear();
 110     }
 111 
 112     /* Create a privileged secondary finalizer thread in the system thread
 113        group for the given Runnable, and wait for it to complete.
 114 
 115        This method is used by both runFinalization and runFinalizersOnExit.
 116        The former method invokes all pending finalizers, while the latter
 117        invokes all uninvoked finalizers if on-exit finalization has been
 118        enabled.


 138                         Thread.currentThread().interrupt();
 139                     }
 140                     return null;
 141                 }});
 142     }
 143 
 144     /* Called by Runtime.runFinalization() */
 145     static void runFinalization() {
 146         if (VM.initLevel() == 0) {
 147             return;
 148         }
 149 
 150         forkSecondaryFinalizer(new Runnable() {
 151             private volatile boolean running;
 152             public void run() {
 153                 // in case of recursive call to run()
 154                 if (running)
 155                     return;
 156                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 157                 running = true;
 158                 for (;;) {
 159                     Finalizer f = (Finalizer)queue.poll();
 160                     if (f == null) break;
 161                     f.runFinalizer(jla);
 162                 }
 163             }
 164         });
 165     }
 166 
 167     /* Invoked by java.lang.Shutdown */
 168     static void runAllFinalizers() {
 169         if (VM.initLevel() == 0) {
 170             return;
 171         }
 172 
 173         forkSecondaryFinalizer(new Runnable() {
 174             private volatile boolean running;
 175             public void run() {
 176                 // in case of recursive call to run()
 177                 if (running)
 178                     return;
 179                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 180                 running = true;
 181                 for (;;) {

 182                     Finalizer f;
 183                     synchronized (lock) {
 184                         f = unfinalized;
 185                         if (f == null) break;
 186                         unfinalized = f.next;



 187                     }
 188                     f.runFinalizer(jla);
 189                 }}});
 190     }
 191 
 192     private static class FinalizerThread extends Thread {
 193         private volatile boolean running;
 194         FinalizerThread(ThreadGroup g) {
 195             super(g, null, "Finalizer", 0, false);
 196         }
 197         public void run() {
 198             // in case of recursive call to run()
 199             if (running)
 200                 return;
 201 
 202             // Finalizer thread starts before System.initializeSystemClass
 203             // is called.  Wait until JavaLangAccess is available
 204             while (VM.initLevel() == 0) {
 205                 // delay until VM completes initialization
 206                 try {
 207                     VM.awaitInitLevel(1);
 208                 } catch (InterruptedException x) {
 209                     // ignore and continue
 210                 }
 211             }
 212             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 213             running = true;
 214             for (;;) {
 215                 try {
 216                     Finalizer f = (Finalizer)queue.remove();
 217                     f.runFinalizer(jla);
 218                 } catch (InterruptedException x) {
 219                     // ignore and continue
 220                 }
 221             }
 222         }
 223     }
 224 
 225     static {
 226         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 227         for (ThreadGroup tgn = tg;
 228              tgn != null;
 229              tg = tgn, tgn = tg.getParent());
 230         Thread finalizer = new FinalizerThread(tg);
 231         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 232         finalizer.setDaemon(true);
 233         finalizer.start();
 234     }
 235 
 236 }


  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 jdk.internal.misc.JavaLangAccess;
  31 import jdk.internal.misc.SharedSecrets;
  32 import jdk.internal.misc.VM;
  33 
  34 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
  35                                                           same package as the Reference
  36                                                           class */
  37 
  38     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
  39 
  40     /** Head of doubly linked list of Finalizers awaiting finalization. */
  41     private static Finalizer unfinalized = null;
  42 
  43     /** Lock guarding access to unfinalized list. */
  44     private static final Object lock = new Object();
  45 
  46     private Finalizer next, prev;


  47 
  48     private Finalizer(Object finalizee) {
  49         super(finalizee, queue);
  50         // push onto unfinalized


  51         synchronized (lock) {
  52             if (unfinalized != null) {
  53                 this.next = unfinalized;
  54                 unfinalized.prev = this;
  55             }
  56             unfinalized = this;
  57         }
  58     }
  59 

























  60     static ReferenceQueue<Object> getQueue() {
  61         return queue;
  62     }
  63 
  64     /* Invoked by VM */
  65     static void register(Object finalizee) {
  66         new Finalizer(finalizee);
  67     }
  68 
  69     private void deregisterAndRunFinalizer(JavaLangAccess jla) {
  70         synchronized (lock) {
  71             if (this.next == this)      // already finalized
  72                 return;
  73             // unlink from unfinalized
  74             if (unfinalized == this)
  75                 unfinalized = this.next;
  76             else
  77                 this.prev.next = this.next;
  78             if (this.next != null)
  79                 this.next.prev = this.prev;
  80             this.prev = null;
  81             this.next = this;           // mark as finalized
  82         }
  83         runFinalizer(jla);
  84     }
  85 
  86     private void runFinalizer(JavaLangAccess jla) {
  87         try {
  88             Object finalizee = this.get();
  89             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
  90                 jla.invokeFinalize(finalizee);
  91 
  92                 /* Clear stack slot containing this variable, to decrease
  93                    the chances of false retention with a conservative GC */
  94                 finalizee = null;
  95             }
  96         } catch (Throwable x) { }
  97         super.clear();
  98     }
  99 
 100     /* Create a privileged secondary finalizer thread in the system thread
 101        group for the given Runnable, and wait for it to complete.
 102 
 103        This method is used by both runFinalization and runFinalizersOnExit.
 104        The former method invokes all pending finalizers, while the latter
 105        invokes all uninvoked finalizers if on-exit finalization has been
 106        enabled.


 126                         Thread.currentThread().interrupt();
 127                     }
 128                     return null;
 129                 }});
 130     }
 131 
 132     /* Called by Runtime.runFinalization() */
 133     static void runFinalization() {
 134         if (VM.initLevel() == 0) {
 135             return;
 136         }
 137 
 138         forkSecondaryFinalizer(new Runnable() {
 139             private volatile boolean running;
 140             public void run() {
 141                 // in case of recursive call to run()
 142                 if (running)
 143                     return;
 144                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 145                 running = true;
 146                 for (Finalizer f; (f = (Finalizer)queue.poll()) != null; )
 147                     f.deregisterAndRunFinalizer(jla);



 148             }
 149         });
 150     }
 151 
 152     /* Invoked by java.lang.Shutdown */
 153     static void runAllFinalizers() {
 154         if (VM.initLevel() == 0) {
 155             return;
 156         }
 157 
 158         forkSecondaryFinalizer(new Runnable() {
 159             private volatile boolean running;
 160             public void run() {
 161                 // in case of recursive call to run()
 162                 if (running)
 163                     return;
 164                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 165                 running = true;
 166                 for (;;) {
 167                     // "pollFirst" from unfinalized
 168                     Finalizer f;
 169                     synchronized (lock) {
 170                         f = unfinalized;
 171                         if (f == null) break;
 172                         unfinalized = f.next;
 173                         if (unfinalized != null)
 174                             unfinalized.prev = null;
 175                         f.next = f; // mark as finalized
 176                     }
 177                     f.runFinalizer(jla);
 178                 }}});
 179     }
 180 
 181     private static class FinalizerThread extends Thread {
 182         private volatile boolean running;
 183         FinalizerThread(ThreadGroup g) {
 184             super(g, null, "Finalizer", 0, false);
 185         }
 186         public void run() {
 187             // in case of recursive call to run()
 188             if (running)
 189                 return;
 190 
 191             // Finalizer thread starts before System.initializeSystemClass
 192             // is called.  Wait until JavaLangAccess is available
 193             while (VM.initLevel() == 0) {
 194                 // delay until VM completes initialization
 195                 try {
 196                     VM.awaitInitLevel(1);
 197                 } catch (InterruptedException x) {
 198                     // ignore and continue
 199                 }
 200             }
 201             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 202             running = true;
 203             for (;;) {
 204                 try {
 205                     Finalizer f = (Finalizer)queue.remove();
 206                     f.deregisterAndRunFinalizer(jla);
 207                 } catch (InterruptedException x) {
 208                     // ignore and continue
 209                 }
 210             }
 211         }
 212     }
 213 
 214     static {
 215         ThreadGroup tg = Thread.currentThread().getThreadGroup();
 216         for (ThreadGroup tgn = tg;
 217              tgn != null;
 218              tg = tgn, tgn = tg.getParent());
 219         Thread finalizer = new FinalizerThread(tg);
 220         finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 221         finalizer.setDaemon(true);
 222         finalizer.start();
 223     }
 224 
 225 }
< prev index next >