--- old/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-05-12 23:11:44.603657661 +0200 +++ new/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-05-12 23:11:44.542658737 +0200 @@ -27,6 +27,10 @@ import java.security.PrivilegedAction; import java.security.AccessController; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + import sun.misc.JavaLangAccess; import sun.misc.ManagedLocalsThread; import sun.misc.SharedSecrets; @@ -186,6 +190,29 @@ }}}); } + static String printFinalizationQueue() { + Map countMap = new HashMap<>(); + queue.forEach(r -> { + Object referent = r.get(); + if (referent != null) { + countMap.computeIfAbsent( + referent.getClass().getName(), k -> new int[1])[0]++; + } + }); + @SuppressWarnings("unchecked") + Map.Entry[] entries = countMap.entrySet().toArray( + new Map.Entry[countMap.size()] + ); + Arrays.sort(entries, (e1, e2) -> + Integer.compare(e2.getValue()[0], e1.getValue()[0])); + StringBuilder sb = new StringBuilder(); + for (Map.Entry e : entries) { + sb.append("Class: ").append(e.getKey()) + .append(" count: ").append(e.getValue()[0]).append("\n"); + } + return sb.toString(); + } + private static class FinalizerThread extends ManagedLocalsThread { private volatile boolean running; FinalizerThread(ThreadGroup g) { --- old/src/java.base/share/classes/java/lang/ref/Reference.java 2015-05-12 23:11:44.878652809 +0200 +++ new/src/java.base/share/classes/java/lang/ref/Reference.java 2015-05-12 23:11:44.767654767 +0200 @@ -94,10 +94,16 @@ volatile ReferenceQueue queue; + /* When a Reference is de-queued from ReferenceQueue, Reference.next + * points to this object. + */ + private static class Dequeued extends Reference { Dequeued() { super(null); } } + static final Reference DEQUEUED = new Dequeued(); + /* When active: NULL * pending: this * Enqueued: next reference in queue (or this if last) - * Inactive: this + * Inactive: DEQUEUED */ @SuppressWarnings("rawtypes") Reference next; --- old/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-05-12 23:11:45.180647480 +0200 +++ new/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-05-12 23:11:45.106648786 +0200 @@ -25,6 +25,8 @@ package java.lang.ref; +import java.util.function.Consumer; + /** * Reference queues, to which registered reference objects are appended by the * garbage collector after the appropriate reachability changes are detected. @@ -83,7 +85,7 @@ null : r.next; // Unchecked due to the next field having a raw type in Reference r.queue = NULL; - r.next = r; + r.next = Reference.DEQUEUED; queueLength--; if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(-1); @@ -164,4 +166,23 @@ return remove(0); } + /** + * Iterate queue and invoke given action with each Reference. + * Suitable for diagnostic purposes. + */ + void forEach(Consumer> action) { + for (Reference r = head; r != null;) { + action.accept(r); + Reference rn = r.next; + if (rn == r) { + // end of chain + r = null; + } else if (rn == Reference.DEQUEUED) { + // restart from head if overtaken by queue poller(s) + r = head; + } else { + r = rn; + } + } + } }