7036199: Adding a notification to the implementation of GarbageCollectorMXBeans
authorfparain
Mon, 16 May 2011 17:28:18 +0200
changeset 9698 8b66cd6c5ebc
parent 9697 2abc44607cf6
child 9699 5dfc211872f4
7036199: Adding a notification to the implementation of GarbageCollectorMXBeans Summary: Add a JMX notification to GarbageCollectorMXBeans Reviewed-by: acorn, mchung
jdk/make/java/management/mapfile-vers
jdk/src/share/classes/com/sun/management/GarbageCollectionNotificationInfo.java
jdk/src/share/classes/sun/management/GarbageCollectionNotifInfoCompositeData.java
jdk/src/share/classes/sun/management/GarbageCollectorImpl.java
jdk/src/share/classes/sun/management/GcInfoCompositeData.java
jdk/src/share/classes/sun/management/MemoryManagerImpl.java
jdk/src/share/classes/sun/management/VMManagement.java
jdk/src/share/classes/sun/management/VMManagementImpl.java
jdk/src/share/javavm/export/jmm.h
jdk/src/share/native/sun/management/GarbageCollectorImpl.c
jdk/src/share/native/sun/management/VMManagementImpl.c
jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationContentTest.java
jdk/test/com/sun/management/GarbageCollectorMXBean/GarbageCollectionNotificationTest.java
--- 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");
+    }
+}