From e075dabb401ce62bdb60a8116277103caaa130d3 Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Mon, 16 May 2011 17:28:18 +0200 Subject: [PATCH] 7036199: Adding a notification to the implementation of GarbageCollectorMXBeans Add a JMX notification to GarbageCollectorMXBeans Reviewed-by: acorn, mchung --- jdk/make/java/management/mapfile-vers | 1 + .../GarbageCollectionNotificationInfo.java | 237 ++++++++++++++++++ ...rbageCollectionNotifInfoCompositeData.java | 219 ++++++++++++++++ .../sun/management/GarbageCollectorImpl.java | 109 +++++++- .../sun/management/GcInfoCompositeData.java | 47 +++- .../sun/management/MemoryManagerImpl.java | 16 +- .../classes/sun/management/VMManagement.java | 1 + .../sun/management/VMManagementImpl.java | 6 + jdk/src/share/javavm/export/jmm.h | 5 +- .../sun/management/GarbageCollectorImpl.c | 16 +- .../native/sun/management/VMManagementImpl.c | 7 + ...bageCollectionNotificationContentTest.java | 163 ++++++++++++ .../GarbageCollectionNotificationTest.java | 128 ++++++++++ 13 files changed, 943 insertions(+), 12 deletions(-) create mode 100644 jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java create mode 100644 jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java create mode 100644 jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java create mode 100644 jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java diff --git a/jdk/make/java/management/mapfile-vers b/jdk/make/java/management/mapfile-vers index 1fb36b78c49..21b92d45f9e 100644 --- a/jdk/make/java/management/mapfile-vers +++ b/jdk/make/java/management/mapfile-vers @@ -49,6 +49,7 @@ SUNWprivate_1.1 { Java_sun_management_Flag_setStringValue; Java_sun_management_GarbageCollectorImpl_getCollectionCount; Java_sun_management_GarbageCollectorImpl_getCollectionTime; + Java_sun_management_GarbageCollectorImpl_setNotificationEnabled; Java_sun_management_GcInfoBuilder_fillGcAttributeInfo; Java_sun_management_GcInfoBuilder_getLastGcInfo0; Java_sun_management_GcInfoBuilder_getNumGcExtAttributes; diff --git a/jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java b/jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java new file mode 100644 index 00000000000..64fb94b3fb8 --- /dev/null +++ b/jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2011, 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 com.sun.management; + +import javax.management.Notification; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataView; +import javax.management.openmbean.CompositeType; +import java.util.Collection; +import java.util.Collections; +import sun.management.GarbageCollectionNotifInfoCompositeData; + +/** + * The information about a garbage collection + * + *

+ * A garbage collection notification is emitted by {@link GarbageCollectorMXBean} + * when the Java virtual machine completes a garbage collection action + * The notification emitted will contain the garbage collection notification + * information about the status of the memory: + * + *

  • The name of the garbage collector used perform the collection.
  • + *
  • The action performed by the garbage collector.
  • + *
  • The cause of the garbage collection action.
  • + *
  • A {@link GcInfo} object containing some statistics about the GC cycle + (start time, end time) and the memory usage before and after + the GC cycle.
  • + * + * + *

    + * A {@link CompositeData CompositeData} representing + * the {@code GarbageCollectionNotificationInfo} object + * is stored in the + * {@linkplain javax.management.Notification#setUserData userdata} + * of a {@linkplain javax.management.Notification notification}. + * The {@link #from from} method is provided to convert from + * a {@code CompositeData} to a {@code GarbageCollectionNotificationInfo} + * object. For example: + * + *

    + *      Notification notif;
    + *
    + *      // receive the notification emitted by a GarbageCollectorMXBean and set to notif
    + *      ...
    + *
    + *      String notifType = notif.getType();
    + *      if (notifType.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
    + *          // retrieve the garbage collection notification information
    + *          CompositeData cd = (CompositeData) notif.getUserData();
    + *          GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from(cd);
    + *          ....
    + *      }
    + * 
    + * + *

    + * The type of the notification emitted by a {@code GarbageCollectorMXBean} is: + *

    + **/ + +public class GarbageCollectionNotificationInfo implements CompositeDataView { + + private final String gcName; + private final String gcAction; + private final String gcCause; + private final GcInfo gcInfo; + private final CompositeData cdata; + + /** + * Notification type denoting that + * the Java virtual machine has completed a garbage collection cycle. + * This notification is emitted by a {@link GarbageCollectorMXBean}. + * The value of this notification type is + * {@code com.sun.management.gc.notification}. + */ + public static final String GARBAGE_COLLECTION_NOTIFICATION = + "com.sun.management.gc.notification"; + + /** + * Constructs a {@code GarbageCollectionNotificationInfo} object. + * + * @param gcName The name of the garbage collector used to perform the collection + * @param gcAction The name of the action performed by the garbage collector + * @param gcCause The cause the garbage collection action + * @param gcInfo a GcInfo object providing statistics about the GC cycle + */ + public GarbageCollectionNotificationInfo(String gcName, + String gcAction, + String gcCause, + GcInfo gcInfo) { + if (gcName == null) { + throw new NullPointerException("Null gcName"); + } + if (gcAction == null) { + throw new NullPointerException("Null gcAction"); + } + if (gcCause == null) { + throw new NullPointerException("Null gcCause"); + } + this.gcName = gcName; + this.gcAction = gcAction; + this.gcCause = gcCause; + this.gcInfo = gcInfo; + this.cdata = new GarbageCollectionNotifInfoCompositeData(this); + } + + GarbageCollectionNotificationInfo(CompositeData cd) { + GarbageCollectionNotifInfoCompositeData.validateCompositeData(cd); + + this.gcName = GarbageCollectionNotifInfoCompositeData.getGcName(cd); + this.gcAction = GarbageCollectionNotifInfoCompositeData.getGcAction(cd); + this.gcCause = GarbageCollectionNotifInfoCompositeData.getGcCause(cd); + this.gcInfo = GarbageCollectionNotifInfoCompositeData.getGcInfo(cd); + this.cdata = cd; + } + + /** + * Returns the name of the garbage collector used to perform the collection + * + * @return the name of the garbage collector used to perform the collection + */ + public String getGcName() { + return gcName; + } + + /** + * Returns the action of the performed by the garbage collector + * + * @return the the action of the performed by the garbage collector + */ + public String getGcAction() { + return gcAction; + } + + /** + * Returns the cause the garbage collection + * + * @return the the cause the garbage collection + */ + public String getGcCause() { + return gcCause; + } + + /** + * Returns the GC information related to the last garbage collection + * + * @return the GC information related to the + * last garbage collection + */ + public GcInfo getGcInfo() { + return gcInfo; + } + + /** + * Returns a {@code GarbageCollectionNotificationInfo} object represented by the + * given {@code CompositeData}. + * The given {@code CompositeData} must contain + * the following attributes: + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Attribute NameType
    gcName{@code java.lang.String}
    gcAction{@code java.lang.String}
    gcCause{@code java.lang.String}
    gcInfo{@code javax.management.openmbean.CompositeData}
    + *
    + * + * @param cd {@code CompositeData} representing a + * {@code GarbageCollectionNotificationInfo} + * + * @throws IllegalArgumentException if {@code cd} does not + * represent a {@code GarbaageCollectionNotificationInfo} object. + * + * @return a {@code GarbageCollectionNotificationInfo} object represented + * by {@code cd} if {@code cd} is not {@code null}; + * {@code null} otherwise. + */ + public static GarbageCollectionNotificationInfo from(CompositeData cd) { + if (cd == null) { + return null; + } + + if (cd instanceof GarbageCollectionNotifInfoCompositeData) { + return ((GarbageCollectionNotifInfoCompositeData) cd).getGarbageCollectionNotifInfo(); + } else { + return new GarbageCollectionNotificationInfo(cd); + } + } + + public CompositeData toCompositeData(CompositeType ct) { + return cdata; + } + +} diff --git a/jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java b/jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java new file mode 100644 index 00000000000..3284b09286e --- /dev/null +++ b/jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2011, 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 sun.management; + +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import java.lang.reflect.Method; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.reflect.Field; +import java.util.HashMap; + +/** + * A CompositeData for GarbageCollectionNotificationInfo for the local management support. + * This class avoids the performance penalty paid to the + * construction of a CompositeData use in the local case. + */ +public class GarbageCollectionNotifInfoCompositeData extends LazyCompositeData { + private final GarbageCollectionNotificationInfo gcNotifInfo; + + public GarbageCollectionNotifInfoCompositeData(GarbageCollectionNotificationInfo info) { + this.gcNotifInfo = info; + } + + public GarbageCollectionNotificationInfo getGarbageCollectionNotifInfo() { + return gcNotifInfo; + } + + public static CompositeData toCompositeData(GarbageCollectionNotificationInfo info) { + GarbageCollectionNotifInfoCompositeData gcnicd = + new GarbageCollectionNotifInfoCompositeData(info); + return gcnicd.getCompositeData(); + } + + private CompositeType getCompositeTypeByBuilder() { + final GcInfoBuilder builder = AccessController.doPrivileged (new PrivilegedAction() { + public GcInfoBuilder run() { + try { + Class cl = Class.forName("com.sun.management.GcInfo"); + Field f = cl.getDeclaredField("builder"); + f.setAccessible(true); + return (GcInfoBuilder)f.get(gcNotifInfo.getGcInfo()); + } catch(ClassNotFoundException e) { + return null; + } catch(NoSuchFieldException e) { + return null; + } catch(IllegalAccessException e) { + return null; + } + } + }); + CompositeType gict = null; + synchronized(compositeTypeByBuilder) { + gict = compositeTypeByBuilder.get(builder); + if(gict == null) { + OpenType[] gcNotifInfoItemTypes = new OpenType[] { + SimpleType.STRING, + SimpleType.STRING, + SimpleType.STRING, + builder.getGcInfoCompositeType(), + }; + try { + final String typeName = + "sun.management.GarbageCollectionNotifInfoCompositeType"; + gict = new CompositeType(typeName, + "CompositeType for GC notification info", + gcNotifInfoItemNames, + gcNotifInfoItemNames, + gcNotifInfoItemTypes); + compositeTypeByBuilder.put(builder,gict); + } catch (OpenDataException e) { + // shouldn't reach here + throw Util.newException(e); + } + } + } + return gict; + } + + protected CompositeData getCompositeData() { + // CONTENTS OF THIS ARRAY MUST BE SYNCHRONIZED WITH + // gcNotifInfoItemNames! + final Object[] gcNotifInfoItemValues; + gcNotifInfoItemValues = new Object[] { + gcNotifInfo.getGcName(), + gcNotifInfo.getGcAction(), + gcNotifInfo.getGcCause(), + GcInfoCompositeData.toCompositeData(gcNotifInfo.getGcInfo()) + }; + + CompositeType gict = getCompositeTypeByBuilder(); + + try { + return new CompositeDataSupport(gict, + gcNotifInfoItemNames, + gcNotifInfoItemValues); + } catch (OpenDataException e) { + // Should never reach here + throw new AssertionError(e); + } + } + + // private static MappedMXBeanType gcInfoMapType; + private static final String GC_NAME = "gcName"; + private static final String GC_ACTION = "gcAction"; + private static final String GC_CAUSE = "gcCause"; + private static final String GC_INFO = "gcInfo"; + private static final String[] gcNotifInfoItemNames = { + GC_NAME, + GC_ACTION, + GC_CAUSE, + GC_INFO + }; + private static HashMap compositeTypeByBuilder = + new HashMap(); + + public static String getGcName(CompositeData cd) { + String gcname = getString(cd, GC_NAME); + if (gcname == null) { + throw new IllegalArgumentException("Invalid composite data: " + + "Attribute " + GC_NAME + " has null value"); + } + return gcname; + } + + public static String getGcAction(CompositeData cd) { + String gcaction = getString(cd, GC_ACTION); + if (gcaction == null) { + throw new IllegalArgumentException("Invalid composite data: " + + "Attribute " + GC_ACTION + " has null value"); + } + return gcaction; + } + + public static String getGcCause(CompositeData cd) { + String gccause = getString(cd, GC_CAUSE); + if (gccause == null) { + throw new IllegalArgumentException("Invalid composite data: " + + "Attribute " + GC_CAUSE + " has null value"); + } + return gccause; + } + + public static GcInfo getGcInfo(CompositeData cd) { + CompositeData gcInfoData = (CompositeData) cd.get(GC_INFO); + return GcInfo.from(gcInfoData); + } + + /** Validate if the input CompositeData has the expected + * CompositeType (i.e. contain all attributes with expected + * names and types). + */ + public static void validateCompositeData(CompositeData cd) { + if (cd == null) { + throw new NullPointerException("Null CompositeData"); + } + + if (!isTypeMatched( getBaseGcNotifInfoCompositeType(), cd.getCompositeType())) { + throw new IllegalArgumentException( + "Unexpected composite type for GarbageCollectionNotificationInfo"); + } + } + + // This is only used for validation. + private static CompositeType baseGcNotifInfoCompositeType = null; + private static synchronized CompositeType getBaseGcNotifInfoCompositeType() { + if (baseGcNotifInfoCompositeType == null) { + try { + OpenType[] baseGcNotifInfoItemTypes = new OpenType[] { + SimpleType.STRING, + SimpleType.STRING, + SimpleType.STRING, + GcInfoCompositeData.getBaseGcInfoCompositeType() + }; + baseGcNotifInfoCompositeType = + new CompositeType("sun.management.BaseGarbageCollectionNotifInfoCompositeType", + "CompositeType for Base GarbageCollectionNotificationInfo", + gcNotifInfoItemNames, + gcNotifInfoItemNames, + baseGcNotifInfoItemTypes); + } catch (OpenDataException e) { + // shouldn't reach here + throw Util.newException(e); + } + } + return baseGcNotifInfoCompositeType; + } + + private static final long serialVersionUID = -1805123446483771292L; +} diff --git a/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java b/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java index dcfd96d5a5d..09c1ffbec4d 100644 --- a/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java +++ b/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -26,6 +26,7 @@ package sun.management; import com.sun.management.GarbageCollectorMXBean; +import com.sun.management.GarbageCollectionNotificationInfo; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; @@ -35,9 +36,15 @@ import javax.management.openmbean.CompositeData; import javax.management.MBeanInfo; import javax.management.MBeanAttributeInfo; import javax.management.ObjectName; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ListenerNotFoundException; import java.util.List; import java.util.ListIterator; +import java.util.Map; /** * Implementation class for the garbage collector. @@ -78,19 +85,111 @@ class GarbageCollectorImpl extends MemoryManagerImpl // Sun JDK extension private GcInfoBuilder gcInfoBuilder; + + private synchronized GcInfoBuilder getGcInfoBuilder() { + if(gcInfoBuilder == null) { + gcInfoBuilder = new GcInfoBuilder(this, getAllPoolNames()); + } + return gcInfoBuilder; + } + public GcInfo getLastGcInfo() { + GcInfo info = getGcInfoBuilder().getLastGcInfo(); + return info; + } + + private final static String notifName = + "javax.management.Notification"; + + private final static String[] gcNotifTypes = { + GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION + }; + + private MBeanNotificationInfo[] notifInfo = null; + public MBeanNotificationInfo[] getNotificationInfo() { synchronized (this) { - if (gcInfoBuilder == null) { - gcInfoBuilder = new GcInfoBuilder(this, getAllPoolNames()); + if (notifInfo == null) { + notifInfo = new MBeanNotificationInfo[1]; + notifInfo[0] = new MBeanNotificationInfo(gcNotifTypes, + notifName, + "GC Notification"); } } + return notifInfo; + } - GcInfo info = gcInfoBuilder.getLastGcInfo(); - return info; + private static long seqNumber = 0; + private static long getNextSeqNumber() { + return ++seqNumber; + } + + void createGCNotification(long timestamp, + String gcName, + String gcAction, + String gcCause, + GcInfo gcInfo) { + + if (!hasListeners()) { + return; + } + + Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, + getObjectName(), + getNextSeqNumber(), + timestamp, + gcName); + GarbageCollectionNotificationInfo info = + new GarbageCollectionNotificationInfo(gcName, + gcAction, + gcCause, + gcInfo); + + CompositeData cd = + GarbageCollectionNotifInfoCompositeData.toCompositeData(info); + notif.setUserData(cd); + sendNotification(notif); + } + + public synchronized void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + { + boolean before = hasListeners(); + super.addNotificationListener(listener, filter, handback); + boolean after = hasListeners(); + if (!before && after) { + setNotificationEnabled(this, true); + } + } + + public synchronized void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + boolean before = hasListeners(); + super.removeNotificationListener(listener); + boolean after = hasListeners(); + if (before && !after) { + setNotificationEnabled(this,false); + } + } + + public synchronized void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws ListenerNotFoundException + { + boolean before = hasListeners(); + super.removeNotificationListener(listener,filter,handback); + boolean after = hasListeners(); + if (before && !after) { + setNotificationEnabled(this,false); + } } public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE, getName()); } + native void setNotificationEnabled(GarbageCollectorMXBean gc, + boolean enabled); + } diff --git a/jdk/src/share/classes/sun/management/GcInfoCompositeData.java b/jdk/src/share/classes/sun/management/GcInfoCompositeData.java index 1fc0301fca4..fb423e1608b 100644 --- a/jdk/src/share/classes/sun/management/GcInfoCompositeData.java +++ b/jdk/src/share/classes/sun/management/GcInfoCompositeData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2011, 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 @@ -27,6 +27,7 @@ package sun.management; import java.lang.management.MemoryUsage; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.util.Iterator; import java.util.Map; import java.util.HashMap; @@ -41,6 +42,9 @@ import javax.management.openmbean.SimpleType; import javax.management.openmbean.OpenType; import javax.management.openmbean.OpenDataException; import com.sun.management.GcInfo; +import com.sun.management.GarbageCollectionNotificationInfo; +import java.security.AccessController; +import java.security.PrivilegedAction; /** * A CompositeData for GcInfo for the local management support. @@ -64,6 +68,44 @@ public class GcInfoCompositeData extends LazyCompositeData { return info; } + public static CompositeData toCompositeData(final GcInfo info) { + final GcInfoBuilder builder = AccessController.doPrivileged (new PrivilegedAction() { + public GcInfoBuilder run() { + try { + Class cl = Class.forName("com.sun.management.GcInfo"); + Field f = cl.getDeclaredField("builder"); + f.setAccessible(true); + return (GcInfoBuilder)f.get(info); + } catch(ClassNotFoundException e) { + return null; + } catch(NoSuchFieldException e) { + return null; + } catch(IllegalAccessException e) { + return null; + } + } + }); + final Object[] extAttr = AccessController.doPrivileged (new PrivilegedAction() { + public Object[] run() { + try { + Class cl = Class.forName("com.sun.management.GcInfo"); + Field f = cl.getDeclaredField("extAttributes"); + f.setAccessible(true); + return (Object[])f.get(info); + } catch(ClassNotFoundException e) { + return null; + } catch(NoSuchFieldException e) { + return null; + } catch(IllegalAccessException e) { + return null; + } + } + }); + GcInfoCompositeData gcicd = + new GcInfoCompositeData(info,builder,extAttr); + return gcicd.getCompositeData(); + } + protected CompositeData getCompositeData() { // CONTENTS OF THIS ARRAY MUST BE SYNCHRONIZED WITH // baseGcInfoItemNames! @@ -115,7 +157,6 @@ public class GcInfoCompositeData extends LazyCompositeData { } } - private static final String ID = "id"; private static final String START_TIME = "startTime"; private static final String END_TIME = "endTime"; @@ -231,7 +272,7 @@ public class GcInfoCompositeData extends LazyCompositeData { // This is only used for validation. private static CompositeType baseGcInfoCompositeType = null; - private static synchronized CompositeType getBaseGcInfoCompositeType() { + static synchronized CompositeType getBaseGcInfoCompositeType() { if (baseGcInfoCompositeType == null) { try { baseGcInfoCompositeType = diff --git a/jdk/src/share/classes/sun/management/MemoryManagerImpl.java b/jdk/src/share/classes/sun/management/MemoryManagerImpl.java index c06f1c55627..a1d0ede3b7d 100644 --- a/jdk/src/share/classes/sun/management/MemoryManagerImpl.java +++ b/jdk/src/share/classes/sun/management/MemoryManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -29,6 +29,7 @@ import java.lang.management.ManagementFactory; import java.lang.management.MemoryManagerMXBean; import java.lang.management.MemoryPoolMXBean; +import javax.management.MBeanNotificationInfo; import javax.management.ObjectName; /** @@ -38,7 +39,8 @@ import javax.management.ObjectName; * ManagementFactory.getMemoryManagerMXBeans() returns a list * of instances of this class. */ -class MemoryManagerImpl implements MemoryManagerMXBean { +class MemoryManagerImpl extends NotificationEmitterSupport + implements MemoryManagerMXBean { private final String name; private final boolean isValid; @@ -76,6 +78,16 @@ class MemoryManagerImpl implements MemoryManagerMXBean { } private native MemoryPoolMXBean[] getMemoryPools0(); + private MBeanNotificationInfo[] notifInfo = null; + public MBeanNotificationInfo[] getNotificationInfo() { + synchronized (this) { + if(notifInfo == null) { + notifInfo = new MBeanNotificationInfo[0]; + } + } + return notifInfo; + } + public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE, getName()); } diff --git a/jdk/src/share/classes/sun/management/VMManagement.java b/jdk/src/share/classes/sun/management/VMManagement.java index 607f6cf88aa..50760baa398 100644 --- a/jdk/src/share/classes/sun/management/VMManagement.java +++ b/jdk/src/share/classes/sun/management/VMManagement.java @@ -45,6 +45,7 @@ public interface VMManagement { public boolean isSynchronizerUsageSupported(); public boolean isThreadAllocatedMemorySupported(); public boolean isThreadAllocatedMemoryEnabled(); + public boolean isGcNotificationSupported(); // Class Loading Subsystem public long getTotalClassCount(); diff --git a/jdk/src/share/classes/sun/management/VMManagementImpl.java b/jdk/src/share/classes/sun/management/VMManagementImpl.java index d5b00ea117f..88566ae99db 100644 --- a/jdk/src/share/classes/sun/management/VMManagementImpl.java +++ b/jdk/src/share/classes/sun/management/VMManagementImpl.java @@ -56,6 +56,8 @@ class VMManagementImpl implements VMManagement { private static boolean objectMonitorUsageSupport; private static boolean synchronizerUsageSupport; private static boolean threadAllocatedMemorySupport; + private static boolean gcNotificationSupport; + static { version = getVersion0(); @@ -100,6 +102,10 @@ class VMManagementImpl implements VMManagement { return threadAllocatedMemorySupport; } + public boolean isGcNotificationSupported() { + return gcNotificationSupport; + } + public native boolean isThreadContentionMonitoringEnabled(); public native boolean isThreadCpuTimeEnabled(); public native boolean isThreadAllocatedMemoryEnabled(); diff --git a/jdk/src/share/javavm/export/jmm.h b/jdk/src/share/javavm/export/jmm.h index df08124e56c..52976113322 100644 --- a/jdk/src/share/javavm/export/jmm.h +++ b/jdk/src/share/javavm/export/jmm.h @@ -48,7 +48,7 @@ enum { JMM_VERSION_1_0 = 0x20010000, JMM_VERSION_1_1 = 0x20010100, // JDK 6 JMM_VERSION_1_2 = 0x20010200, // JDK 7 - JMM_VERSION = 0x20010200 + JMM_VERSION = 0x20010201 }; typedef struct { @@ -293,6 +293,9 @@ typedef struct jmmInterface_1_ { jlongArray ids, jboolean lockedMonitors, jboolean lockedSynchronizers); + void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env, + jobject mgr, + jboolean enabled); } JmmInterface; #ifdef __cplusplus diff --git a/jdk/src/share/native/sun/management/GarbageCollectorImpl.c b/jdk/src/share/native/sun/management/GarbageCollectorImpl.c index 76c3238ee8b..020335d32ea 100644 --- a/jdk/src/share/native/sun/management/GarbageCollectorImpl.c +++ b/jdk/src/share/native/sun/management/GarbageCollectorImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -36,3 +36,17 @@ JNIEXPORT jlong JNICALL Java_sun_management_GarbageCollectorImpl_getCollectionTi (JNIEnv *env, jobject mgr) { return jmm_interface->GetLongAttribute(env, mgr, JMM_GC_TIME_MS); } + + +JNIEXPORT void JNICALL Java_sun_management_GarbageCollectorImpl_setNotificationEnabled +(JNIEnv *env, jobject dummy, jobject gc,jboolean enabled) { + + if (gc == NULL) { + JNU_ThrowNullPointerException(env, "Invalid GarbageCollectorMBean"); + return; + } + if((jmm_version > JMM_VERSION_1_2) + || (jmm_version == JMM_VERSION_1_2 && ((jmm_version&0xFF)>=1))) { + jmm_interface->SetGCNotificationEnabled(env, gc, enabled); + } +} diff --git a/jdk/src/share/native/sun/management/VMManagementImpl.c b/jdk/src/share/native/sun/management/VMManagementImpl.c index 295d93c34f5..1deb7c8c8ca 100644 --- a/jdk/src/share/native/sun/management/VMManagementImpl.c +++ b/jdk/src/share/native/sun/management/VMManagementImpl.c @@ -95,6 +95,13 @@ Java_sun_management_VMManagementImpl_initOptionalSupportFields value = mos.isThreadAllocatedMemorySupported; setStaticBooleanField(env, cls, "threadAllocatedMemorySupport", value); + + if ((jmm_version > JMM_VERSION_1_2) || + (jmm_version == JMM_VERSION_1_2 && ((jmm_version&0xFF) >= 1))) { + setStaticBooleanField(env, cls, "gcNotificationSupport", JNI_TRUE); + } else { + setStaticBooleanField(env, cls, "gcNotificationSupport", JNI_FALSE); + } } JNIEXPORT jobjectArray JNICALL diff --git a/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java new file mode 100644 index 00000000000..6d3386b0905 --- /dev/null +++ b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* + * @test + * @bug 7036199 + * @summary Check that GarbageCollectionNotification contents are reasonable + * @author Frederic Parain + * @run main/othervm GarbageCollectionNotificationContentTest + */ + +import java.util.*; +import java.lang.management.*; +import java.lang.reflect.*; +import javax.management.*; +import javax.management.openmbean.*; +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.reflect.Field; + +public class GarbageCollectionNotificationContentTest { + private static HashMap listenerInvoked + = new HashMap(); + static volatile long count = 0; + static volatile long number = 0; + static Object synchronizer = new Object(); + + static class GcListener implements NotificationListener { + public void handleNotification(Notification notif, Object handback) { + String type = notif.getType(); + if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + GarbageCollectionNotificationInfo gcNotif = + GarbageCollectionNotificationInfo.from((CompositeData) notif.getUserData()); + String source = ((ObjectName)notif.getSource()).getCanonicalName(); + synchronized(synchronizer) { + if(listenerInvoked.get(source) == null) { + listenerInvoked.put(((ObjectName)notif.getSource()).getCanonicalName(),gcNotif); + count++; + if(count >= number) { + synchronizer.notify(); + } + } + } + } + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final Boolean isNotificationSupported = AccessController.doPrivileged (new PrivilegedAction() { + public Boolean run() { + try { + Class cl = Class.forName("sun.management.VMManagementImpl"); + Field f = cl.getDeclaredField("gcNotificationSupport"); + f.setAccessible(true); + return f.getBoolean(null); + } catch(ClassNotFoundException e) { + return false; + } catch(NoSuchFieldException e) { + return false; + } catch(IllegalAccessException e) { + return false; + } + } + }); + if(!isNotificationSupported) { + System.out.println("GC Notification not supported by the JVM, test skipped"); + return; + } + final ObjectName gcMXBeanPattern = + new ObjectName("java.lang:type=GarbageCollector,*"); + Set names = + mbs.queryNames(gcMXBeanPattern, null); + if (names.isEmpty()) + throw new Exception("Test incorrect: no GC MXBeans"); + number = names.size(); + for (ObjectName n : names) { + if(mbs.isInstanceOf(n,"javax.management.NotificationEmitter")) { + listenerInvoked.put(n.getCanonicalName(),null); + GcListener listener = new GcListener(); + mbs.addNotificationListener(n, listener, null, null); + } + } + // Invocation of System.gc() to trigger major GC + System.gc(); + // Allocation of many short living and small objects to trigger minor GC + Object data[] = new Object[32]; + for(int i = 0; i<100000000; i++) { + data[i%32] = new int[8]; + } + int wakeup = 0; + synchronized(synchronizer) { + while(count != number) { + synchronizer.wait(10000); + wakeup++; + if(wakeup > 10) + break; + } + } + for (GarbageCollectionNotificationInfo notif : listenerInvoked.values() ) { + checkGarbageCollectionNotificationInfoContent(notif); + } + System.out.println("Test passed"); + } + + private static void checkGarbageCollectionNotificationInfoContent(GarbageCollectionNotificationInfo notif) throws Exception { + System.out.println("GC notification for "+notif.getGcName()); + System.out.print("Action: "+notif.getGcAction()); + System.out.println(" Cause: "+notif.getGcCause()); + GcInfo info = notif.getGcInfo(); + System.out.print("GC Info #" + info.getId()); + System.out.print(" start:" + info.getStartTime()); + System.out.print(" end:" + info.getEndTime()); + System.out.println(" (" + info.getDuration() + "ms)"); + Map usage = info.getMemoryUsageBeforeGc(); + + List pnames = new ArrayList(); + for (Map.Entry entry : usage.entrySet() ) { + String poolname = (String) entry.getKey(); + pnames.add(poolname); + MemoryUsage busage = (MemoryUsage) entry.getValue(); + MemoryUsage ausage = (MemoryUsage) info.getMemoryUsageAfterGc().get(poolname); + if (ausage == null) { + throw new RuntimeException("After Gc Memory does not exist" + + " for " + poolname); + } + System.out.println("Usage for pool " + poolname); + System.out.println(" Before GC: " + busage); + System.out.println(" After GC: " + ausage); + } + + // check if memory usage for all memory pools are returned + List pools = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean p : pools ) { + if (!pnames.contains(p.getName())) { + throw new RuntimeException("GcInfo does not contain " + + "memory usage for pool " + p.getName()); + } + } + } +} diff --git a/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java new file mode 100644 index 00000000000..bad4e4076b0 --- /dev/null +++ b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* + * @test + * @bug 7036199 + * @summary Check that GarbageCollection notification are thrown by every GarbageCollectorMXBean + * @author Frederic Parain + * @run main/othervm GarbageCollectionNotificationTest + */ + +import java.util.*; +import java.lang.management.*; +import java.lang.reflect.*; +import javax.management.*; +import javax.management.openmbean.*; +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.reflect.Field; + +public class GarbageCollectionNotificationTest { + private static HashMap listenerInvoked = new HashMap(); + static volatile long count = 0; + static volatile long number = 0; + static Object synchronizer = new Object(); + + static class GcListener implements NotificationListener { + public void handleNotification(Notification notif, Object handback) { + String type = notif.getType(); + if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + GarbageCollectionNotificationInfo gcNotif = + GarbageCollectionNotificationInfo.from((CompositeData) notif.getUserData()); + String source = ((ObjectName)notif.getSource()).getCanonicalName(); + synchronized(synchronizer) { + if(!listenerInvoked.get(source)) { + listenerInvoked.put(((ObjectName)notif.getSource()).getCanonicalName(),true); + count++; + if(count >= number) { + synchronizer.notify(); + } + } + } + } + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final Boolean isNotificationSupported = AccessController.doPrivileged (new PrivilegedAction() { + public Boolean run() { + try { + Class cl = Class.forName("sun.management.VMManagementImpl"); + Field f = cl.getDeclaredField("gcNotificationSupport"); + f.setAccessible(true); + return f.getBoolean(null); + } catch(ClassNotFoundException e) { + return false; + } catch(NoSuchFieldException e) { + return false; + } catch(IllegalAccessException e) { + return false; + } + } + }); + if(!isNotificationSupported) { + System.out.println("GC Notification not supported by the JVM, test skipped"); + return; + } + final ObjectName gcMXBeanPattern = + new ObjectName("java.lang:type=GarbageCollector,*"); + Set names = + mbs.queryNames(gcMXBeanPattern, null); + if (names.isEmpty()) + throw new Exception("Test incorrect: no GC MXBeans"); + number = names.size(); + for (ObjectName n : names) { + if(mbs.isInstanceOf(n,"javax.management.NotificationEmitter")) { + listenerInvoked.put(n.getCanonicalName(),false); + GcListener listener = new GcListener(); + mbs.addNotificationListener(n, listener, null, null); + } + } + // Invocation of System.gc() to trigger major GC + System.gc(); + // Allocation of many short living and small objects to trigger minor GC + Object data[] = new Object[32]; + for(int i = 0; i<100000000; i++) { + data[i%32] = new int[8]; + } + int wakeup = 0; + synchronized(synchronizer) { + while(count != number) { + synchronizer.wait(10000); + wakeup++; + if(wakeup > 10) + break; + } + } + for (String source : listenerInvoked.keySet()) { + if(!listenerInvoked.get(source)) + throw new Exception("Test incorrect: notifications have not been sent for " + + source); + } + System.out.println("Test passed"); + } +}