--- old/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-06-06 15:34:10.680931393 +0200 +++ new/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-06-06 15:34:10.590932969 +0200 @@ -27,98 +27,101 @@ 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 { /* Package-private; must be in - same package as the Reference - class */ - - private static ReferenceQueue 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 extends FinalReference 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. + group for the given Runnable, and wait for it to complete. - This method is used by both runFinalization and runFinalizersOnExit. - The former method invokes all pending finalizers, while the latter - invokes all uninvoked finalizers if on-exit finalization has been - enabled. - - These two methods could have been implemented by offloading their work - to the regular finalizer thread and waiting for that thread to finish. - The advantage of creating a fresh thread, however, is that it insulates - invokers of these methods from a stalled or deadlocked finalizer thread. - */ + This method is used by both runFinalization and runFinalizersOnExit. + The former method invokes all pending finalizers, while the latter + invokes all uninvoked finalizers if on-exit finalization has been + enabled. + + These two methods could have been implemented by offloading their work + to the regular finalizer thread and waiting for that thread to finish. + The advantage of creating a fresh thread, however, is that it insulates + invokers of these methods from a stalled or deadlocked finalizer thread. + */ private static void forkSecondaryFinalizer(final Runnable proc) { AccessController.doPrivileged( new PrivilegedAction<>() { @@ -150,13 +153,8 @@ // 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(); } }); } @@ -173,61 +171,79 @@ // 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"); - } - 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 - } - } - final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - running = true; - for (;;) { - try { - Finalizer f = (Finalizer)queue.remove(); - f.runFinalizer(jla); - } catch (InterruptedException x) { - // ignore and continue - } - } + // 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; } - 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 isAlive() { + return getReferentVolatile() != null; + } + + boolean isDeleted() { + return getReferentVolatile() == null; + } + + 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); + } + + 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 fc = Finalizer.class; + prevOffset = UNSAFE.objectFieldOffset(fc.getDeclaredField("prev")); + nextOffset = UNSAFE.objectFieldOffset(fc.getDeclaredField("next")); + Class tc = Thread.class; + threadLocalRandomSecondarySeedOffset = UNSAFE.objectFieldOffset + (tc.getDeclaredField("threadLocalRandomSecondarySeed")); + } catch (Exception e) { + throw new Error(e); + } + } }