package refproc; import sun.misc.Unsafe; import java.lang.ref.PhantomCleaner; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.LongAdder; /** * A base for reference processing benchmarks. */ public class BenchBase { static final LongAdder constructions = new LongAdder(); static final LongAdder destructions = new LongAdder(); static final Object poolInfo; static { Object pInfo; try { Class rhpc = Class.forName("java.lang.ref.ReferenceHandling"); Field f = rhpc.getDeclaredField("pool"); f.setAccessible(true); ForkJoinPool p = (ForkJoinPool) f.get(null); pInfo = new Object() { @Override public String toString() { return String.format("%5d %5d %10d", p.getActiveThreadCount(), p.getPoolSize(), p.getQueuedTaskCount()); } }; } catch (Exception e) { pInfo = ""; } poolInfo = pInfo; } static final class AutoCleanable implements AutoCloseable { public static AutoCleanable newClosed() { try (AutoCleanable ac = new AutoCleanable()) { return ac; } } public AutoCleanable() { constructions.increment(); } @Override public void close() { destructions.increment(); } } static final class Finalizable { public Finalizable() { constructions.increment(); } @Override protected void finalize() throws Throwable { destructions.increment(); } } static final class PhantomCleanable implements AutoCloseable { static class CL extends PhantomCleaner { CL(PhantomCleanable referent) { super(referent); } @Override protected void doClean() { destructions.increment();; } } public static PhantomCleanable newClosed() { try (PhantomCleanable pc = new PhantomCleanable()) { return pc; } } final CL cl; public PhantomCleanable() { cl = new CL(this); constructions.increment(); } @Override public void close() { cl.clean(); } } static final class WeakRefClearable { static final ReferenceQueue queue = new ReferenceQueue<>(); static final DLList[] refLists; 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; refLists = new DLList[lists]; for (int i = 0; i < lists; i++) { refLists[i] = new DLList(); } } public WeakRefClearable() { constructions.increment(); int rnd = ThreadLocalRandom.current().nextInt(); int listIndex = (rnd >>> 1) & (refLists.length - 1); refLists[listIndex].link(new WeakRefNode(this, listIndex), (rnd & 1) == 0); } static final class WeakRefNode extends WeakReference implements DLList.Node { private final int listIndex; public WeakRefNode(WeakRefClearable referent, int listIndex) { super(referent, queue); this.listIndex = listIndex; } void remove() { if (delete()) { refLists[listIndex].unlink(this); destructions.increment(); } } // DLList.Node implementation private volatile DLList.Node prev, next; private volatile int deleted; @Override public DLList.Node getPrev() { return prev; } @Override public void lazySetPrev(DLList.Node prev) { UNSAFE.putOrderedObject(this, prevOffset, prev); } @Override public boolean casPrev(DLList.Node cmp, DLList.Node val) { return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val); } @Override public DLList.Node getNext() { return next; } @Override public void lazySetNext(DLList.Node next) { UNSAFE.putOrderedObject(this, nextOffset, next); } @Override public boolean casNext(DLList.Node cmp, DLList.Node val) { return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } @Override public boolean isDeleted() { return deleted == 1; } @Override public boolean delete() { return (deleted == 0) && UNSAFE.compareAndSwapInt(this, deletedOffset, 0, 1); } private static final sun.misc.Unsafe UNSAFE; private static final long prevOffset; private static final long nextOffset; private static final long deletedOffset; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); UNSAFE = (Unsafe) f.get(null); Class wrnc = WeakRefNode.class; prevOffset = UNSAFE.objectFieldOffset(wrnc.getDeclaredField("prev")); nextOffset = UNSAFE.objectFieldOffset(wrnc.getDeclaredField("next")); deletedOffset = UNSAFE.objectFieldOffset(wrnc.getDeclaredField("deleted")); } catch (Exception e) { throw new Error(e); } } } static { Thread expunger = new Thread() { @Override public void run() { while (true) { try { Reference ref = queue.remove(); ((WeakRefNode) ref).remove(); } catch (InterruptedException e) { // ignore } } } }; expunger.setPriority(Thread.MAX_PRIORITY); expunger.setDaemon(true); expunger.start(); } } static class ConstructionThread extends Thread { private final int instancesToConstruct; private final CountDownLatch finishedLatch; private final int ratePerMs; private final Runnable constructor; ConstructionThread(int instancesToConstruct, CountDownLatch finishedLatch, int ratePerMs, Runnable constructor) { this.instancesToConstruct = instancesToConstruct; this.finishedLatch = finishedLatch; this.ratePerMs = ratePerMs; this.constructor = constructor; } @Override public void run() { for (int i = 0; i < instancesToConstruct; i += ratePerMs) { for (int j = 0; j < ratePerMs; j++) { constructor.run(); } try { Thread.sleep(1L); } catch (InterruptedException e) { // ignore } } finishedLatch.countDown(); } } static void printThroughputWhileConstructionsLessThan(long maxConstructions) { System.out.printf(" ~5s interval cumulative active pool queued\n"); System.out.printf(" t[ms] construct. destruct. in-flight con./ms des./ms con./ms des./ms thr. size tasks\n"); System.out.printf(" ------- ---------- ---------- ---------- ------- ------- ------- ------- ----- ----- ----------\n"); long c0 = 0L; long d0 = 0L; long t00 = System.nanoTime(); long t0 = t00; while (c0 < maxConstructions || d0 < maxConstructions) { try { Thread.sleep(5000L); } catch (InterruptedException e) { } long t = System.nanoTime(); long c = constructions.longValue(); long d = destructions.longValue(); long dc = c - c0; long dd = d - d0; long dt = t - t0; long tc = t - t00; System.out.printf("%8d %10d %10d %10d %7d %7d %7d %7d %s\n", (t - t00) / 1000_000L, c, d, c - d, dc * 1000_000L / dt, dd * 1000_000L / dt, c * 1000_000L / tc, d * 1000_000L / tc, poolInfo); c0 = c; d0 = d; t0 = t; if (c >= maxConstructions) { System.gc(); } } } }