< 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 >