< prev index next >

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

Print this page

        

@@ -25,87 +25,90 @@
 
 package java.lang.ref;
 
 import java.security.PrivilegedAction;
 import java.security.AccessController;
-import sun.misc.JavaLangAccess;
+import java.util.concurrent.ThreadLocalRandom;
 import sun.misc.ManagedLocalsThread;
 import sun.misc.SharedSecrets;
 import sun.misc.VM;
 
-final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
-                                                          same package as the Reference
-                                                          class */
-
-    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
-    private static Finalizer unfinalized = null;
-    private static final Object lock = new Object();
-
-    private Finalizer
-        next = null,
-        prev = null;
-
-    private boolean hasBeenFinalized() {
-        return (next == this);
-    }
-
-    private void add() {
-        synchronized (lock) {
-            if (unfinalized != null) {
-                this.next = unfinalized;
-                unfinalized.prev = this;
-            }
-            unfinalized = this;
+/* Package-private; must be in same package as the Reference class */
+class Finalizer<T> extends FinalReference<T> implements Runnable {
+    /**
+     * Finalizers are registered in a doubly-linked list so that they are kept
+     * alive until discovered by VM, processed by ReferenceHandling pool and then
+     * unlinked. There are several lists to distribute Finalizers randomly into
+     * to reduce contention among concurrent threads trying to link/unlink them.
+     */
+    static final FinalizerList[] unfinalized;
+    static {
+        int cpus = Runtime.getRuntime().availableProcessors();
+        // smallest power of two equal or greater than 2 * # of CPUs
+        int lists = (cpus <= 1) ? 2 : Integer.highestOneBit(cpus - 1) << 2;
+        unfinalized = new FinalizerList[lists];
+        for (int i = 0; i < lists; i++) {
+            unfinalized[i] = new FinalizerList();
         }
     }
 
-    private void remove() {
-        synchronized (lock) {
-            if (unfinalized == this) {
-                if (this.next != null) {
-                    unfinalized = this.next;
-                } else {
-                    unfinalized = this.prev;
-                }
-            }
-            if (this.next != null) {
-                this.next.prev = this.prev;
-            }
-            if (this.prev != null) {
-                this.prev.next = this.next;
-            }
-            this.next = this;   /* Indicates that this has been finalized */
-            this.prev = this;
-        }
+    volatile Finalizer prev;
+    volatile Finalizer next;
+    private final int listIndex;
+
+    Finalizer(T finalizee, int listIndex) {
+        super(finalizee, ReferenceQueue.NULL);
+        this.listIndex = listIndex;
     }
 
-    private Finalizer(Object finalizee) {
-        super(finalizee, queue);
-        add();
+    /** A constructor used for special Finalizer instances in FinalizerList */
+    Finalizer() {
+        super(null, ReferenceQueue.NULL);
+        listIndex = -1; // never registered in any list
     }
 
-    /* Invoked by VM */
+    /** Invoked by VM for objects overriding finalize() method */
     static void register(Object finalizee) {
-        new Finalizer(finalizee);
+        int rnd = nextSecondarySeed();
+        int index = (rnd >>> 1) & (unfinalized.length - 1);
+        unfinalized[index].link(new Finalizer<>(finalizee, index), (rnd & 1) == 0);
     }
 
-    private void runFinalizer(JavaLangAccess jla) {
-        synchronized (this) {
-            if (hasBeenFinalized()) return;
-            remove();
+    @Override
+    public void run() {
+        T finalizee = delete();
+        if (finalizee == null) {
+            return;
         }
+        unfinalized[listIndex].unlink(this);
         try {
-            Object finalizee = this.get();
-            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
-                jla.invokeFinalize(finalizee);
+            if (!(finalizee instanceof java.lang.Enum)) {
+                invokeFinalizee(finalizee);
 
                 /* Clear stack slot containing this variable, to decrease
                    the chances of false retention with a conservative GC */
                 finalizee = null;
             }
         } catch (Throwable x) { }
-        super.clear();
+    }
+
+    /* Invoke the finalize() method on the finalizee (overridden by Finalizator) */
+    void invokeFinalizee(T finalizee) throws Throwable {
+        SharedSecrets.getJavaLangAccess().invokeFinalize(finalizee);
+        finalizee = null;
+    }
+
+    @Override
+    public void clear() {
+        T finalizee = delete();
+        if (finalizee == null) {
+            return;
+        }
+        unfinalized[listIndex].unlink(this);
+        /* Clear stack slot containing this variable, to decrease
+           the chances of false retention with a conservative GC */
+        finalizee = null;
     }
 
     /* Create a privileged secondary finalizer thread in the system thread
        group for the given Runnable, and wait for it to complete.
 

@@ -148,17 +151,12 @@
             private volatile boolean running;
             public void run() {
                 // in case of recursive call to run()
                 if (running)
                     return;
-                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                 running = true;
-                for (;;) {
-                    Finalizer f = (Finalizer)queue.poll();
-                    if (f == null) break;
-                    f.runFinalizer(jla);
-                }
+                ReferenceHandling.runFinalization();
             }
         });
     }
 
     /* Invoked by java.lang.Shutdown */

@@ -171,63 +169,81 @@
             private volatile boolean running;
             public void run() {
                 // in case of recursive call to run()
                 if (running)
                     return;
-                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                 running = true;
-                for (;;) {
-                    Finalizer f;
-                    synchronized (lock) {
-                        f = unfinalized;
-                        if (f == null) break;
-                        unfinalized = f.next;
-                    }
-                    f.runFinalizer(jla);
+                for (FinalizerList uflist : unfinalized)
+                    for (Finalizer<?> f = uflist.first(); f != null; f = uflist.succ(f)) {
+                        f.run();
                 }}});
     }
 
-    private static class FinalizerThread extends ManagedLocalsThread {
-        private volatile boolean running;
-        FinalizerThread(ThreadGroup g) {
-            super(g, "Finalizer");
+    // Unsafe mechanics
+
+    /**
+     * Returns the pseudo-randomly initialized or updated secondary seed.
+     * Copied from ThreadLocalRandom due to package access restrictions.
+     */
+    static int nextSecondarySeed() {
+        int r;
+        Thread t = Thread.currentThread();
+        if ((r = UNSAFE.getInt(t, threadLocalRandomSecondarySeedOffset)) != 0) {
+            r ^= r << 13;   // xorshift
+            r ^= r >>> 17;
+            r ^= r << 5;
+        }
+        else if ((r = ThreadLocalRandom.current().nextInt()) == 0)
+            r = 1; // avoid zero
+        UNSAFE.putInt(t, threadLocalRandomSecondarySeedOffset, r);
+        return r;
         }
-        public void run() {
-            // in case of recursive call to run()
-            if (running)
-                return;
 
-            // Finalizer thread starts before System.initializeSystemClass
-            // is called.  Wait until JavaLangAccess is available
-            while (!VM.isBooted()) {
-                // delay until VM completes initialization
-                try {
-                    VM.awaitBooted();
-                } catch (InterruptedException x) {
-                    // ignore and continue
+    boolean isAlive() {
+        return getReferentVolatile() != null;
                 }
+
+    boolean isDeleted() {
+        return getReferentVolatile() == null;
             }
-            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-            running = true;
-            for (;;) {
-                try {
-                    Finalizer f = (Finalizer)queue.remove();
-                    f.runFinalizer(jla);
-                } catch (InterruptedException x) {
-                    // ignore and continue
+
+    private T delete() {
+        T referent = getReferentVolatile();
+        return (referent != null) && casReferent(referent, null)
+            ? referent : null;
                 }
+
+    void lazySetNext(Finalizer<?> val) {
+        UNSAFE.putOrderedObject(this, nextOffset, val);
             }
+
+    boolean casNext(Finalizer<?> cmp, Finalizer<?> val) {
+        return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
         }
+
+    void lazySetPrev(Finalizer<?> val) {
+        UNSAFE.putOrderedObject(this, prevOffset, val);
     }
 
-    static {
-        ThreadGroup tg = Thread.currentThread().getThreadGroup();
-        for (ThreadGroup tgn = tg;
-             tgn != null;
-             tg = tgn, tgn = tg.getParent());
-        Thread finalizer = new FinalizerThread(tg);
-        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
-        finalizer.setDaemon(true);
-        finalizer.start();
+    boolean casPrev(Finalizer<?> cmp, Finalizer<?> val) {
+        return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val);
     }
 
+    private static final sun.misc.Unsafe UNSAFE;
+    private static final long prevOffset;
+    private static final long nextOffset;
+    private static final long threadLocalRandomSecondarySeedOffset;
+
+    static {
+        try {
+            UNSAFE = sun.misc.Unsafe.getUnsafe();
+            Class<Finalizer> fc = Finalizer.class;
+            prevOffset = UNSAFE.objectFieldOffset(fc.getDeclaredField("prev"));
+            nextOffset = UNSAFE.objectFieldOffset(fc.getDeclaredField("next"));
+            Class<Thread> tc = Thread.class;
+            threadLocalRandomSecondarySeedOffset = UNSAFE.objectFieldOffset
+                (tc.getDeclaredField("threadLocalRandomSecondarySeed"));
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
 }
< prev index next >