From 47bdec1c0e5f94ba91315eb4292b8eceb59bdba4 Mon Sep 17 00:00:00 2001 From: Dmitry Samersoff Date: Thu, 30 Jul 2015 16:29:13 +0300 Subject: [PATCH 1/2] 8059036: Implement Diagnostic Commands for heap and finalizerinfo Implement Diagnostic Commands for heap and finalizerinfo Reviewed-by: sla, plevart, mchung --- .../classes/java/lang/ref/Finalizer.java | 6 +- .../java/lang/ref/FinalizerHistogram.java | 80 ++++++++++++++ .../classes/java/lang/ref/Reference.java | 4 +- .../classes/java/lang/ref/ReferenceQueue.java | 39 ++++++- .../java/lang/ref/FinalizerHistogramTest.java | 104 ++++++++++++++++++ 5 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 jdk/src/java.base/share/classes/java/lang/ref/FinalizerHistogram.java create mode 100644 jdk/test/java/lang/ref/FinalizerHistogramTest.java diff --git a/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java b/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java index d9cabe61543..3bd631226c9 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java @@ -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 @@ -83,6 +83,10 @@ final class Finalizer extends FinalReference { /* Package-private; must add(); } + static ReferenceQueue getQueue() { + return queue; + } + /* Invoked by VM */ static void register(Object finalizee) { new Finalizer(finalizee); diff --git a/jdk/src/java.base/share/classes/java/lang/ref/FinalizerHistogram.java b/jdk/src/java.base/share/classes/java/lang/ref/FinalizerHistogram.java new file mode 100644 index 00000000000..e5c95c1c8c4 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/ref/FinalizerHistogram.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.Comparator; + +/** + * This FinalizerHistogram class is for GC.finalizer_info diagnostic command support. + * It is invoked by the VM. + */ + +final class FinalizerHistogram { + + private static final class Entry { + private int instanceCount; + private final String className; + + int getInstanceCount() { + return instanceCount; + } + + void increment() { + instanceCount += 1; + } + + Entry(String className) { + this.className = className; + } + } + + // Method below is called by VM and VM expect certain + // entry class layout. + + static Entry[] getFinalizerHistogram() { + Map countMap = new HashMap<>(); + ReferenceQueue queue = Finalizer.getQueue(); + queue.forEach(r -> { + Object referent = r.get(); + if (referent != null) { + countMap.computeIfAbsent( + referent.getClass().getName(), Entry::new).increment(); + /* Clear stack slot containing this variable, to decrease + the chances of false retention with a conservative GC */ + referent = null; + } + }); + + Entry fhe[] = countMap.values().toArray(new Entry[countMap.size()]); + Arrays.sort(fhe, + Comparator.comparingInt(Entry::getInstanceCount).reversed()); + return fhe; + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java index 7f6456f0947..854cdd6cb9d 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java @@ -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 @@ -101,7 +101,7 @@ public abstract class Reference { * 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) diff --git a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index 534d21c7b2b..bf1af798fd4 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -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,12 @@ public class ReferenceQueue { } } - @SuppressWarnings("unchecked") private Reference reallyPoll() { /* Must hold lock */ Reference r = head; if (r != null) { - head = (r.next == r) ? - null : - r.next; // Unchecked due to the next field having a raw type in Reference + @SuppressWarnings("unchecked") + Reference rn = r.next; + head = (rn == r) ? null : rn; r.queue = NULL; r.next = r; queueLength--; @@ -164,4 +165,32 @@ public class ReferenceQueue { 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. + */ + void forEach(Consumer> action) { + for (Reference r = head; r != null;) { + action.accept(r); + @SuppressWarnings("unchecked") + 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; + } + } + } } diff --git a/jdk/test/java/lang/ref/FinalizerHistogramTest.java b/jdk/test/java/lang/ref/FinalizerHistogramTest.java new file mode 100644 index 00000000000..a201331f47a --- /dev/null +++ b/jdk/test/java/lang/ref/FinalizerHistogramTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import java.lang.reflect.Method; +import java.lang.reflect.Field; + +/* + * @test + * @summary Unit test for FinalizerHistogram + * @run main FinalizerHistogramTest + */ + +public class FinalizerHistogramTest { + static ReentrantLock lock = new ReentrantLock(); + static volatile int wasInitialized = 0; + static volatile int wasTrapped = 0; + static final int objectsCount = 1000; + + static class MyObject { + public MyObject() { + // Make sure object allocation/deallocation is not optimized out + wasInitialized += 1; + } + + protected void finalize() { + // Trap the object in a finalization queue + wasTrapped += 1; + lock.lock(); + } + } + + public static void main(String[] argvs) { + try { + lock.lock(); + for(int i = 0; i < objectsCount; ++i) { + new MyObject(); + } + System.out.println("Objects intialized: " + objectsCount); + System.gc(); + while(wasTrapped < 1); + + Class klass = Class.forName("java.lang.ref.FinalizerHistogram"); + + Method m = klass.getDeclaredMethod("getFinalizerHistogram"); + m.setAccessible(true); + Object entries[] = (Object[]) m.invoke(null); + + Class entryKlass = Class.forName("java.lang.ref.FinalizerHistogram$Entry"); + Field name = entryKlass.getDeclaredField("className"); + name.setAccessible(true); + Field count = entryKlass.getDeclaredField("instanceCount"); + count.setAccessible(true); + + System.out.println("Unreachable instances waiting for finalization"); + System.out.println("#instances class name"); + System.out.println("-----------------------"); + + boolean found = false; + for (Object entry : entries) { + Object e = entryKlass.cast(entry); + System.out.printf("%10d %s\n", count.get(e), name.get(e)); + if (((String) name.get(e)).indexOf("MyObject") != -1 ) { + found = true; + } + } + + if (!found) { + throw new RuntimeException("MyObject is not found in test output"); + } + + System.out.println("Test PASSED"); + } catch(Exception e) { + System.err.println("Test failed with " + e); + e.printStackTrace(System.err); + throw new RuntimeException("Test failed"); + } finally { + lock.unlock(); + } + } +} From 71f82b9db5d1cc129df46245e911f6abb39e9381 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 31 Jul 2015 15:29:34 +0200 Subject: [PATCH 2/2] 8129215: com.sun.jmx.mbeanserver.Introspector may provide results inconsistent with the JavaBeans Introspector Reviewed-by: dfuchs --- .../com/sun/jmx/mbeanserver/Introspector.java | 13 ++- .../mbeanserver/introspector/BeanClass.java | 28 +++++ .../introspector/SimpleIntrospectorTest.java | 104 ++++++++++++++++++ 3 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 jdk/test/com/sun/jmx/mbeanserver/introspector/BeanClass.java create mode 100644 jdk/test/com/sun/jmx/mbeanserver/introspector/SimpleIntrospectorTest.java diff --git a/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java index e3c273328fc..92dc59c0b64 100644 --- a/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java +++ b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java @@ -552,8 +552,10 @@ public class Introspector { // Java Beans introspection // Class clazz = complex.getClass(); - Method readMethod = JavaBeansAccessor.getReadMethod(clazz, element); - if (readMethod == null) { + Method readMethod; + if (JavaBeansAccessor.isAvailable()) { + readMethod = JavaBeansAccessor.getReadMethod(clazz, element); + } else { // Java Beans not available so use simple introspection // to locate method readMethod = SimpleIntrospector.getReadMethod(clazz, element); @@ -676,7 +678,12 @@ public class Introspector { * {@code null} if no method is found. */ static Method getReadMethod(Class clazz, String property) { - // first character in uppercase (compatibility with JavaBeans) + if (Character.isUpperCase(property.charAt(0))) { + // the property name must start with a lower-case letter + return null; + } + // first character after 'get/is' prefix must be in uppercase + // (compatibility with JavaBeans) property = property.substring(0, 1).toUpperCase(Locale.ENGLISH) + property.substring(1); String getMethod = GET_METHOD_PREFIX + property; diff --git a/jdk/test/com/sun/jmx/mbeanserver/introspector/BeanClass.java b/jdk/test/com/sun/jmx/mbeanserver/introspector/BeanClass.java new file mode 100644 index 00000000000..06746f64923 --- /dev/null +++ b/jdk/test/com/sun/jmx/mbeanserver/introspector/BeanClass.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +public class BeanClass { + public int getNumber() {return 1;} + public boolean isAvailable() {return false;} +} diff --git a/jdk/test/com/sun/jmx/mbeanserver/introspector/SimpleIntrospectorTest.java b/jdk/test/com/sun/jmx/mbeanserver/introspector/SimpleIntrospectorTest.java new file mode 100644 index 00000000000..1b463f585a2 --- /dev/null +++ b/jdk/test/com/sun/jmx/mbeanserver/introspector/SimpleIntrospectorTest.java @@ -0,0 +1,104 @@ + +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.lang.reflect.Method; + +/* + * @test + * @bug 8129215 + * @summary The test checks whether the SimpleIntrospector is honoring the + * the JavaBeans property naming convention of always starting + * with a lower-case letter + * + * @author Jaroslav Bachorik + * @modules java.management + * @run clean SimpleIntrospectorTest + * @run build SimpleIntrospectorTest BeanClass + * @run main SimpleIntrospectorTest + */ +public class SimpleIntrospectorTest { + private static Method INTROSPECT_GETTER; + + public static void main(String ... args) throws Exception { + Class clz = Class.forName( + "com.sun.jmx.mbeanserver.Introspector$SimpleIntrospector" + ); + INTROSPECT_GETTER = clz.getDeclaredMethod( + "getReadMethod", + Class.class, + String.class + ); + INTROSPECT_GETTER.setAccessible(true); + boolean result = true; + result &= checkNumberValid(); + result &= checkNumberInvalid(); + result &= checkAvailableValid(); + result &= checkAvailableInvalid(); + + if (!result) { + throw new Error(); + } + } + + private static boolean checkNumberValid() throws Exception { + return checkGetter(false, "number"); + } + + private static boolean checkNumberInvalid() throws Exception { + return checkGetter(true, "Number"); + } + + private static boolean checkAvailableValid() throws Exception { + return checkGetter(false, "available"); + } + + private static boolean checkAvailableInvalid() throws Exception { + return checkGetter(true, "Available"); + } + + private static boolean checkGetter(boolean nullExpected, String name) + throws Exception { + Method m = getReadMethod(BeanClass.class, name); + boolean result = (m != null); + if (nullExpected) result = !result; + + if (result) { + return true; + } + if (nullExpected) { + System.err.println("SimpleIntrospector resolved an unknown getter " + + "for attribute '"+ name +"'"); + } else { + System.err.println("SimpleIntrospector fails to resolve getter " + + "for attribute '"+ name +"'"); + } + return false; + } + + private static Method getReadMethod(Class clz, String attr) + throws Exception { + return (Method)INTROSPECT_GETTER.invoke(null, clz, attr); + } +}