7036199: Adding a notification to the implementation of GarbageCollectorMXBeans
Summary: Add a JMX notification to GarbageCollectorMXBeans
Reviewed-by: acorn, mchung
--- a/jdk/make/java/management/mapfile-vers Mon May 16 13:10:59 2011 +0100
+++ b/jdk/make/java/management/mapfile-vers Mon May 16 17:28:18 2011 +0200
@@ -49,6 +49,7 @@
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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java Mon May 16 17:28:18 2011 +0200
@@ -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
+ *
+ * <p>
+ * 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:
+ * <u1>
+ * <li>The name of the garbage collector used perform the collection.</li>
+ * <li>The action performed by the garbage collector.</li>
+ * <li>The cause of the garbage collection action.</li>
+ * <li>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.</li>
+ * </u1>
+ *
+ * <p>
+ * 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:
+ *
+ * <blockquote><pre>
+ * 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);
+ * ....
+ * }
+ * </pre></blockquote>
+ *
+ * <p>
+ * The type of the notification emitted by a {@code GarbageCollectorMXBean} is:
+ * <ul>
+ * <li>A {@linkplain #GARBAGE_COLLECTION_NOTIFICATION garbage collection notification}.
+ * <br>Used by every notification emitted by the garbage collector, the details about
+ * the notification are provided in the {@linkplain #getGcAction action} String
+ * <p></li>
+ * </ul>
+ **/
+
+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:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th align=left>Attribute Name</th>
+ * <th align=left>Type</th>
+ * </tr>
+ * <tr>
+ * <td>gcName</td>
+ * <td>{@code java.lang.String}</td>
+ * </tr>
+ * <tr>
+ * <td>gcAction</td>
+ * <td>{@code java.lang.String}</td>
+ * </tr>
+ * <tr>
+ * <td>gcCause</td>
+ * <td>{@code java.lang.String}</td>
+ * </tr>
+ * <tr>
+ * <td>gcInfo</td>
+ * <td>{@code javax.management.openmbean.CompositeData}</td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * @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;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java Mon May 16 17:28:18 2011 +0200
@@ -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<GcInfoBuilder>() {
+ 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<GcInfoBuilder,CompositeType> compositeTypeByBuilder =
+ new HashMap<GcInfoBuilder,CompositeType>();
+
+ 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;
+}
--- a/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/classes/sun/management/GarbageCollectorImpl.java Mon May 16 17:28:18 2011 +0200
@@ -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.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 @@
// 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);
+
}
--- a/jdk/src/share/classes/sun/management/GcInfoCompositeData.java Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/classes/sun/management/GcInfoCompositeData.java Mon May 16 17:28:18 2011 +0200
@@ -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 @@
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.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 @@
return info;
}
+ public static CompositeData toCompositeData(final GcInfo info) {
+ final GcInfoBuilder builder = AccessController.doPrivileged (new PrivilegedAction<GcInfoBuilder>() {
+ 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<Object[]>() {
+ 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 @@
}
}
-
private static final String ID = "id";
private static final String START_TIME = "startTime";
private static final String END_TIME = "endTime";
@@ -231,7 +272,7 @@
// 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 =
--- a/jdk/src/share/classes/sun/management/MemoryManagerImpl.java Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/classes/sun/management/MemoryManagerImpl.java Mon May 16 17:28:18 2011 +0200
@@ -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.MemoryManagerMXBean;
import java.lang.management.MemoryPoolMXBean;
+import javax.management.MBeanNotificationInfo;
import javax.management.ObjectName;
/**
@@ -38,7 +39,8 @@
* 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 @@
}
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());
}
--- a/jdk/src/share/classes/sun/management/VMManagement.java Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/classes/sun/management/VMManagement.java Mon May 16 17:28:18 2011 +0200
@@ -45,6 +45,7 @@
public boolean isSynchronizerUsageSupported();
public boolean isThreadAllocatedMemorySupported();
public boolean isThreadAllocatedMemoryEnabled();
+ public boolean isGcNotificationSupported();
// Class Loading Subsystem
public long getTotalClassCount();
--- a/jdk/src/share/classes/sun/management/VMManagementImpl.java Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/classes/sun/management/VMManagementImpl.java Mon May 16 17:28:18 2011 +0200
@@ -56,6 +56,8 @@
private static boolean objectMonitorUsageSupport;
private static boolean synchronizerUsageSupport;
private static boolean threadAllocatedMemorySupport;
+ private static boolean gcNotificationSupport;
+
static {
version = getVersion0();
@@ -100,6 +102,10 @@
return threadAllocatedMemorySupport;
}
+ public boolean isGcNotificationSupported() {
+ return gcNotificationSupport;
+ }
+
public native boolean isThreadContentionMonitoringEnabled();
public native boolean isThreadCpuTimeEnabled();
public native boolean isThreadAllocatedMemoryEnabled();
--- a/jdk/src/share/javavm/export/jmm.h Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/javavm/export/jmm.h Mon May 16 17:28:18 2011 +0200
@@ -48,7 +48,7 @@
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 @@
jlongArray ids,
jboolean lockedMonitors,
jboolean lockedSynchronizers);
+ void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env,
+ jobject mgr,
+ jboolean enabled);
} JmmInterface;
#ifdef __cplusplus
--- a/jdk/src/share/native/sun/management/GarbageCollectorImpl.c Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/native/sun/management/GarbageCollectorImpl.c Mon May 16 17:28:18 2011 +0200
@@ -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 @@
(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);
+ }
+}
--- a/jdk/src/share/native/sun/management/VMManagementImpl.c Mon May 16 13:10:59 2011 +0100
+++ b/jdk/src/share/native/sun/management/VMManagementImpl.c Mon May 16 17:28:18 2011 +0200
@@ -95,6 +95,13 @@
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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java Mon May 16 17:28:18 2011 +0200
@@ -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<String,GarbageCollectionNotificationInfo> listenerInvoked
+ = new HashMap<String,GarbageCollectionNotificationInfo>();
+ 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<Boolean>() {
+ 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<ObjectName> 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<String, MemoryUsage> usage = info.getMemoryUsageBeforeGc();
+
+ List<String> pnames = new ArrayList<String>();
+ 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<MemoryPoolMXBean> 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());
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java Mon May 16 17:28:18 2011 +0200
@@ -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<String,Boolean> listenerInvoked = new HashMap<String,Boolean>();
+ 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<Boolean>() {
+ 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<ObjectName> 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");
+ }
+}