Mercurial > hg > release > icedtea7-forest-2.2 > jdk
changeset 4406:e0c3fd538f1f
7036199: Adding a notification to the implementation of GarbageCollectorMXBeans
Summary: Add a JMX notification to GarbageCollectorMXBeans
Reviewed-by: acorn, mchung
line wrap: on
line diff
--- a/make/java/management/mapfile-vers Mon May 16 13:10:59 2011 +0100 +++ b/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/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/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/src/share/classes/sun/management/GarbageCollectorImpl.java Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/classes/sun/management/GcInfoCompositeData.java Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/classes/sun/management/MemoryManagerImpl.java Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/classes/sun/management/VMManagement.java Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/classes/sun/management/VMManagementImpl.java Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/javavm/export/jmm.h Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/native/sun/management/GarbageCollectorImpl.c Mon May 16 13:10:59 2011 +0100 +++ b/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/src/share/native/sun/management/VMManagementImpl.c Mon May 16 13:10:59 2011 +0100 +++ b/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/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/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"); + } +}