package java.lang.ref; import java.util.Objects; /** * A {@link PhantomReference} based {@link Cleaner}. *

* PhantomCleaner(s) are a lightweight and more robust alternative to finalization. * They are lightweight because they are not created by the VM and thus do not * require a JNI upcall to be created. They are more robust because they are * phantom references, the weakest type of reference object, thereby avoiding the * nasty ordering problems inherent to finalization. *

* Some time after the GC detects that PhantomCleaner's referent has * become phantom-reachable, one of the finalizing thread(s) will * {@link #clean() invoke} the cleaner. * PhantomCleaner(s) may also be {@link #clean() invoked} directly. The 1st * invocation will {@link #clear()} the PhantomCleaner and invoke the * {@link #doClean() clean-up action}. Subsequent invocations are ignored. * * @since 1.9 */ public abstract class PhantomCleaner extends PhantomReference implements Cleaner { /** * Creates and returns a PhantomCleaner implementation that takes a * {@code Runnable thunk} to be run as a clean-up action. * Some time after the {@code referent} becomes phantom-reachable, one of * the finalizing threads runs the {@code thunk}. * * @param referent the referent object to be tracked * @param thunk The cleanup code to be run when the cleaner is invoked. The * cleanup code is run from one of the finalizing thread(s) * that are also used to run other cleaners and finalization or * from user code that invokes the cleaner directly. * @return The new PhantomCleaner * @throws NullPointerException if {@code referent} or {@code thunk} is null. */ public static PhantomCleaner create(Object referent, Runnable thunk) { Objects.requireNonNull(thunk); return new PhantomCleaner(referent) { @Override protected void doClean() { thunk.run(); } }; } /** * A constructor taking a referent to track. * * @param referent the referent to track * @throws NullPointerException if {@code referent} is null */ public PhantomCleaner(Object referent) { super(Objects.requireNonNull(referent), null); link(); } /** * Invoked at most once after the referent of this * {@code PhantomCleaner} is found phantom-reachable or when the user * explicitly invokes {@link #clean()}. Subclasses should implement this * method and place clean-up actions into it. */ protected abstract void doClean(); /** * Runs this PhantomCleaner, if it has not been run before. This method may be * invoked by one of the finalizing thread(s) or manually by a client * thread. Only the 1st invocation will {@link #clear()} this PhantomCleaner * and invoke {@link #doClean() cleanup-code}. Any unchecked * exception thrown from the {@link #doClean() cleanup-code} will be propagated. * When such exception happens as a result of running this method automatically * by one of the finalizing thread(s), it is ignored. */ @Override public final void clean() { if (delete()) { super.clear(); unlink(); doClean(); } } @Override public final void clear() { if (delete()) { super.clear(); unlink(); } } // Methods and state that enable PhantomCleaner to be an element of DLList @SuppressWarnings("unused") // assigned through Unsafe private volatile Reference prevDll, nextDll; @SuppressWarnings("unused") // assigned through Unsafe private volatile int deletedDll; private boolean delete() { return deletedDll == 0 && UNSAFE.compareAndSwapInt(this, DELETED_DLL, 0, 1); } boolean isDeletedDll() { return deletedDll == 1; } Reference getPrevDll() { return prevDll; } void lazySetPrevDll(Reference val) { UNSAFE.putOrderedObject(this, PREV_DLL, val); } boolean casPrevDll(Reference cmp, Reference val) { return UNSAFE.compareAndSwapObject(this, PREV_DLL, cmp, val); } Reference getNextDll() { return nextDll; } void lazySetNextDll(Reference val) { UNSAFE.putOrderedObject(this, NEXT_DLL, val); } boolean casNextDll(Reference cmp, Reference val) { return UNSAFE.compareAndSwapObject(this, NEXT_DLL, cmp, val); } // Unsafe machinery private static final sun.misc.Unsafe UNSAFE; private static final long PREV_DLL; private static final long NEXT_DLL; private static final long DELETED_DLL; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class pcc = PhantomCleaner.class; PREV_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("prevDll")); NEXT_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("nextDll")); DELETED_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("deletedDll")); } catch (Exception e) { throw new Error(e); } } }