--- old/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-05-31 14:37:41.703318441 +0300 +++ new/src/java.base/share/classes/java/lang/ref/Finalizer.java 2015-05-31 14:37:41.311310406 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,11 @@ import sun.misc.SharedSecrets; import sun.misc.VM; +import java.util.Map; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Arrays; + final class Finalizer extends FinalReference { /* Package-private; must be in same package as the Reference class */ @@ -219,6 +224,50 @@ } } + // Support for GC.finalizer_info DCMD + + // Fileds of the class below is accessed directly by VM + // using hardcoded field offsets, so don't change + // class layout. + static class FinalizerHistogramEntry { + private final int count; + private final String name; + + int getCount() { return count; } + + FinalizerHistogramEntry(Map.Entry e) { + name = e.getKey(); + count = e.getValue()[0]; + } + }; + + // This method called by VM from FinalizerInfoDCmd and + @SuppressWarnings({"unchecked","rawtypes"}) + static Object[] getFinalizerHistogram() { + Map countMap = new HashMap<>(); + queue.forEach(r -> { + Object referent = r.get(); + if (referent != null) { + countMap.computeIfAbsent( + referent.getClass().getName(), k -> new int[1])[0]++; + /* Clear stack slot containing this variable, to decrease + the chances of false retention with a conservative GC */ + referent = null; + } + }); + + // Copy HashMap entry set to array of objects accessible from VM + FinalizerHistogramEntry fhe[] = new FinalizerHistogramEntry[countMap.size()]; + int i = 0; + for (Map.Entry e : countMap.entrySet()) { + fhe[i++] = new FinalizerHistogramEntry(e); + } + + Arrays.sort(fhe, (e1, e2) -> Integer.compare(e2.getCount(), e1.getCount())); + return fhe; + } + + static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; --- old/src/java.base/share/classes/java/lang/ref/Reference.java 2015-05-31 14:37:42.519335169 +0300 +++ new/src/java.base/share/classes/java/lang/ref/Reference.java 2015-05-31 14:37:42.120326990 +0300 @@ -100,7 +100,7 @@ * Inactive: this */ @SuppressWarnings("rawtypes") - Reference next; + volatile Reference next; /* When active: next element in a discovered reference list maintained by GC (or this if last) * pending: next element in the pending list (or null if last) --- old/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-05-31 14:37:43.345352103 +0300 +++ new/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-05-31 14:37:42.952344046 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -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. @@ -75,13 +77,14 @@ } } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) private Reference reallyPoll() { /* Must hold lock */ Reference r = head; if (r != null) { - head = (r.next == r) ? + Reference rn = r.next; + head = (rn == r) ? null : - r.next; // Unchecked due to the next field having a raw type in Reference + rn; // Unchecked due to the next field having a raw type in Reference r.queue = NULL; r.next = r; queueLength--; @@ -164,4 +167,32 @@ return remove(0); } + /** + * Iterate queue and invoke given action with each Reference. + * Suitable for diagnostic purposes. + * WARNING: any use of this method should make sure to not + * retain the referents of iterated references (in case of + * FinalReference(s)) so that their life is not prolonged more + * than necessary. + */ + @SuppressWarnings({"unchecked","rawtypes"}) + void forEach(Consumer> action) { + for (Reference r = head; r != null;) { + action.accept(r); + Reference rn = r.next; + if (rn == r) { + if (r.queue == ENQUEUED) { + // still enqueued -> we reached end of chain + r = null; + } else { + // already dequeued: r.queue == NULL; -> + // restart from head when overtaken by queue poller(s) + r = head; + } + } else { + // next in chain + r = rn; + } + } + } }