A small program that prints the attributes of all JVM MBeans

A few weeks ago I blogged about how to programmatically access the JVM Monitoring information. Here is a small Java application that prints all the attributes of all the JVM Management & Monitoring MBeans.

The sample simply prints the attributes of the JVM MBeans from the JVM in which it runs. It would be very easy to make it print the information of a remote JVM. You would just need to copy the code from the JVMRuntimeClient shown in my previous blog:

     public static void main(String[] args) throws Exception {
        // Parse arguments.
        final ConnectionArgs cArgs = new ConnectionArgs(args);
        
        // Get target's URL
        final JMXServiceURL target = cArgs.getJMXServiceURL();
        
        // Connect to target (assuming no security)
        final JMXConnector connector = JMXConnectorFactory.connect(target);
        
        // Get an MBeanServerConnection on the remote VM.
        final MBeanServerConnection remote = 
                connector.getMBeanServerConnection();
        
        displayAll(conn,new ObjectName("java.lang:\*"));
     }

This sample is composed of 4 small classes:

  1. TextDataDisplay.java: displays attribute values as text. Uses a property-like syntax to display complex values, such as CompositeData, TabularData, Map, Collection, and array of thoses.
  2. MBeanDataDisplay.java: displays all attributes of a given MBean. Uses TextDataDisplay to display individual values.
  3. JVMMBeanDataDisplay.java: extends MBeanDataDisplay, and implements a special case for displaying ThreadMXBean attributes. This is a custom MBeanDataDisplay that has some special knowledge of the JVM MBeans. In particular, for the ThreadMXBean, instead of simply displaying the value of the attribute "AllThreadIds" (an array of longs) it gets and displays the corresponding ThreadInfo for each Thread ID.
  4. Main.java: implements the displayAll() method.

Here is a small extract of what the program prints:

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

MBean: java.lang:type=Memory
{
   # HeapMemoryUsage
   HeapMemoryUsage=CompositeData(java.lang.management.MemoryUsage)
   {
    HeapMemoryUsage.committed=132644864
    HeapMemoryUsage.init=134217728
    HeapMemoryUsage.max=957743104
    HeapMemoryUsage.used=692080
   }

   # NonHeapMemoryUsage
   NonHeapMemoryUsage=CompositeData(java.lang.management.MemoryUsage)
   {
    NonHeapMemoryUsage.committed=18350080
    NonHeapMemoryUsage.init=18350080
    NonHeapMemoryUsage.max=100663296
    NonHeapMemoryUsage.used=3245944
   }

   # ObjectPendingFinalizationCount
   ObjectPendingFinalizationCount=0

   # Verbose
   Verbose=false

}

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

MBean: java.lang:type=GarbageCollector,name=PS MarkSweep
{
   # CollectionCount
   CollectionCount=0

   # CollectionTime
   CollectionTime=0

   # LastGcInfo
   LastGcInfo=null

   # MemoryPoolNames
   MemoryPoolNames[0]=PS Eden Space
   MemoryPoolNames[1]=PS Survivor Space
   MemoryPoolNames[2]=PS Old Gen
   MemoryPoolNames[3]=PS Perm Gen

   # Name
   Name=PS MarkSweep

   # Valid
   Valid=true

}

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

Feel free to experiment with this and to invent your own way of displaying the attribute values!

Update: See also Sundar's blog on how to programmatically dump heap from Java applications.

Main.java

/\*
 \* Main.java
 \*
 \* Created on May 2, 2007, 1:30 PM
 \*
 \* SCCS: %W%
 \*
 \* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 \*
 \* Redistribution and use in source and binary forms, with or without
 \* modification, are permitted provided that the following conditions
 \* are met:
 \*
 \*   - Redistributions of source code must retain the above copyright
 \*     notice, this list of conditions and the following disclaimer.
 \*
 \*   - Redistributions in binary form must reproduce the above copyright
 \*     notice, this list of conditions and the following disclaimer in the
 \*     documentation and/or other materials provided with the distribution.
 \*
 \*   - Neither the name of Sun Microsystems nor the names of its
 \*     contributors may be used to endorse or promote products derived
 \*     from this software without specific prior written permission.
 \*
 \* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 \* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 \* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 \* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 \* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 \* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 \* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 \* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 \* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 \* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 \* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \*/

package opentextdisplay;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.logging.Logger;
import javax.management.JMException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

/\*\*
 \* Class Main
 \* @author Sun Microsystems, Inc.
 \*/
public class Main {
    
    /\*\*
     \* A logger for this class.
     \*\*/
    private static final Logger LOG =
            Logger.getLogger(Main.class.getName());
    public final static String separator = 
            "\\n\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\\n";
    
    public static void displayAll(MBeanServerConnection conn,
            ObjectName pattern) throws IOException, JMException {
        final JVMMBeanDataDisplay display = new JVMMBeanDataDisplay(conn);
        System.out.println(separator);
        for (ObjectName mbean : conn.queryNames(pattern,null)) {
            System.out.println(display.toString(mbean));
            System.out.println(separator);
        }
    }
    
    /\*\* Creates a new instance of Main \*/
    public Main() {
    }
    
    /\*\*
     \* @param args the command line arguments
     \*/
    public static void main(String[] args) throws Exception {
        final MBeanServerConnection conn = 
                ManagementFactory.getPlatformMBeanServer();
        displayAll(conn,new ObjectName("java.lang:\*"));
    }
    
}

TextDataDisplay.java

/\*
 \* TextDataDisplay.java
 \*
 \* Created on May 2, 2007, 1:31 PM / dfuchs
 \*
 \* SCCS: %W%
 \*
 \* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 \*
 \* Redistribution and use in source and binary forms, with or without
 \* modification, are permitted provided that the following conditions
 \* are met:
 \*
 \*   - Redistributions of source code must retain the above copyright
 \*     notice, this list of conditions and the following disclaimer.
 \*
 \*   - Redistributions in binary form must reproduce the above copyright
 \*     notice, this list of conditions and the following disclaimer in the
 \*     documentation and/or other materials provided with the distribution.
 \*
 \*   - Neither the name of Sun Microsystems nor the names of its
 \*     contributors may be used to endorse or promote products derived
 \*     from this software without specific prior written permission.
 \*
 \* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 \* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 \* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 \* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 \* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 \* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 \* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 \* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 \* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 \* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 \* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \*/

package opentextdisplay;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

/\*\*
 \* Class TextDataDisplay
 \* @author Sun Microsystems, Inc.
 \*/
public class TextDataDisplay {
    
    /\*\*
     \* A logger for this class.
     \*\*/
    private static final Logger LOG =
            Logger.getLogger(TextDataDisplay.class.getName());
    
    /\*\* Creates a new instance of TextDataDisplay \*/
    public TextDataDisplay() {
    }
    
    public String display(String name, Object data) {
        return display("",name,data);
    }
    
    public String display(String prefix, String name, Object data) {
        if (name == null) 
            throw new IllegalArgumentException("name must not be null");
        final StringBuffer buffer = new StringBuffer();
        return write(buffer,"",name,data).toString();
    }

    public StringBuffer write(StringBuffer buffer, String prefix, 
            String name, Object data) {
        if (data == null) return writeSimple(buffer,prefix,name,null,true);
        if (data.getClass().isArray()) 
            return writeArray(buffer,prefix,name,data);
        if (data instanceof CompositeData)
            return writeCompositeData(buffer,prefix,name,(CompositeData)data);
        if (data instanceof TabularData)
            return writeTabularData(buffer,prefix,name,(TabularData)data);
        if (data instanceof Map)
            return writeMap(buffer,prefix,name,(Map)data);
        if (data instanceof Collection) {
            return writeArray(buffer,prefix,name,((Collection)data).toArray());
        }
        return writeSimple(buffer,prefix,name,data,true);
    }
    
    String toString(Object data) {
        if (data==null) return "null";
        else return data.toString();
    }
    
    StringBuffer writeSimple(StringBuffer buffer, String prefix, 
            String name, Object data, boolean writeline) {
        buffer.append(prefix).append(name).append("=").append(toString(data));
        if (writeline) buffer.append("\\n");
        return buffer;
    }
    
    StringBuffer writeArray(StringBuffer buffer, String prefix, 
            String name, Object array) {
        if (array == null) 
            return writeSimple(buffer,prefix,name,null,true);
        final int length = Array.getLength(array);
        for (int i=0;i<length;i++) {
            final Object data = Array.get(array,i);
            write(buffer,prefix,name+"["+i+"]",data);
        }
        return buffer;
    }
    
    StringBuffer writeCompositeData(StringBuffer buffer, 
            String prefix, String name, CompositeData data) {
        if (data == null)
            return writeSimple(buffer,prefix,name,null,true);
        writeSimple(buffer,prefix,name,"CompositeData("+
                data.getCompositeType().getTypeName()+")",true);
        buffer.append(prefix).append("{").append("\\n");
        final String fieldprefix = prefix + " ";
        for (String key : data.getCompositeType().keySet()) {
            write(buffer,fieldprefix,name+"."+key,data.get(key));
        }
        buffer.append(prefix).append("}").append("\\n");
        return buffer;
    }
    
    StringBuffer writeTabularData(StringBuffer buffer, 
            String prefix, String name, TabularData data) {
        if (data == null)
            return writeSimple(buffer,prefix,name,null,true);
        writeSimple(buffer,prefix,name,"TabularData("+
                data.getTabularType().getTypeName()+")",true);
        final List<String> keyNames = data.getTabularType().getIndexNames();
        final int indexCount = keyNames.size();
        for (Object keys : data.keySet()) {
            final Object[] keyValues = ((List<?>)keys).toArray();
            final StringBuilder b = new StringBuilder(name);
            b.append("[");
            for (int i=0;i<indexCount;i++) {
                if (i>0) b.append(", ");
                b.append(keyNames.get(i)+"="+keyValues[i]);
            }
            b.append("]");
            writeCompositeData(buffer,prefix,b.toString(),data.get(keyValues));
            b.append("\\n");
        }
        return buffer;
    }
    
    StringBuffer writeMap(StringBuffer buffer, 
            String prefix, String name, Map<Object,Object> data) {
        if (data == null)
            return writeSimple(buffer,prefix,name,null,true);
        writeSimple(buffer,prefix,name,"java.util.Map",true);
        for (Entry<Object,Object> e : data.entrySet()) {
            write(buffer,prefix,name+"["+e.getKey()+"]",e.getValue());
        }
        return buffer;
    }
    
}

MBeanDataDisplay.java

/\*
 \* MBeanDataDisplay.java
 \*
 \* Created on May 2, 2007, 2:49 PM / dfuchs
 \*
 \* SCCS: %W%
 \*
 \* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 \*
 \* Redistribution and use in source and binary forms, with or without
 \* modification, are permitted provided that the following conditions
 \* are met:
 \*
 \*   - Redistributions of source code must retain the above copyright
 \*     notice, this list of conditions and the following disclaimer.
 \*
 \*   - Redistributions in binary form must reproduce the above copyright
 \*     notice, this list of conditions and the following disclaimer in the
 \*     documentation and/or other materials provided with the distribution.
 \*
 \*   - Neither the name of Sun Microsystems nor the names of its
 \*     contributors may be used to endorse or promote products derived
 \*     from this software without specific prior written permission.
 \*
 \* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 \* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 \* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 \* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 \* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 \* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 \* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 \* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 \* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 \* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 \* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \*/
package opentextdisplay;

import java.io.IOException;
import java.util.logging.Logger;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

/\*\*
 \* Class MBeanDataDisplay
 \* @author Sun Microsystems, Inc.
 \*/
public class MBeanDataDisplay {
    
    /\*\*
     \* A logger for this class.
     \*\*/
    private static final Logger LOG =
            Logger.getLogger(MBeanDataDisplay.class.getName());
    
    final MBeanServerConnection server;
    final TextDataDisplay dataDisplay = new TextDataDisplay();
    /\*\* Creates a new instance of MBeanDataDisplay \*/
    public MBeanDataDisplay(MBeanServerConnection conn) {
        this.server = conn;
    }
    
    StringBuffer writeAttribute(StringBuffer buffer, 
            String prefix, ObjectName mbean,
            MBeanAttributeInfo info, Object value) {
            buffer.append(prefix).append("# ").append(info.getDescription())
                .append("\\n");
            return dataDisplay.write(buffer,prefix,info.getName(),value);
    }
    
    public StringBuffer write(StringBuffer buffer, String prefix, ObjectName mbean) 
        throws IOException, JMException {
        final MBeanInfo info = server.getMBeanInfo(mbean);
        buffer.append(prefix).append("MBean: ").append(mbean).append("\\n");
        buffer.append(prefix).append("{\\n");
        final String attrPrefix = prefix+"   ";
        final MBeanAttributeInfo[] attributes = info.getAttributes();
        for (MBeanAttributeInfo attr : attributes) {
            Object toWrite = null;
            try {
                toWrite = server.getAttribute(mbean,attr.getName());
            } catch (Exception x) {
                toWrite = x;
            }
            writeAttribute(buffer,attrPrefix,mbean,attr,toWrite);
            buffer.append("\\n");
        }
        buffer.append(prefix).append("}\\n");
        return buffer;
    }
    
    public String toString(String prefix, ObjectName mbean) 
        throws IOException, JMException {
        return write(new StringBuffer(),prefix,mbean).toString();
    }
    
    public String toString(ObjectName mbean) 
        throws IOException, JMException {
        return toString("",mbean);
    }
}

JVMMBeanDataDisplay.java

/\*
 \* JVMMBeanDataDisplay.java
 \*
 \* Created on May 4, 2007, 4:59 PM / dfuchs
 \*
 \* SCCS: %W%
 \*
 \* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 \*
 \* Redistribution and use in source and binary forms, with or without
 \* modification, are permitted provided that the following conditions
 \* are met:
 \*
 \*   - Redistributions of source code must retain the above copyright
 \*     notice, this list of conditions and the following disclaimer.
 \*
 \*   - Redistributions in binary form must reproduce the above copyright
 \*     notice, this list of conditions and the following disclaimer in the
 \*     documentation and/or other materials provided with the distribution.
 \*
 \*   - Neither the name of Sun Microsystems nor the names of its
 \*     contributors may be used to endorse or promote products derived
 \*     from this software without specific prior written permission.
 \*
 \* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 \* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 \* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 \* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 \* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 \* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 \* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 \* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 \* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 \* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 \* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \*/

package opentextdisplay;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.logging.Logger;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

/\*\*
 \* Class JVMMBeanDataDisplay
 \* 
 \* @author Sun Microsystems, Inc.
 \*/
public class JVMMBeanDataDisplay extends MBeanDataDisplay {
    
    /\*\*
     \* A logger for this class.
     \*\*/
    private static final Logger LOG =
            Logger.getLogger(JVMMBeanDataDisplay.class.getName());
   
    private final static ObjectName THREAD_MXBEAN_NAME;
    static {
        try {
            THREAD_MXBEAN_NAME = 
                    new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
        } catch (Exception x) {
            throw new ExceptionInInitializerError(x);
        }
    }
    
    /\*\*
     \* Creates a new instance of JVMMBeanDataDisplay
     \*/
    public JVMMBeanDataDisplay(MBeanServerConnection conn) {
        super(conn);
    }
    
    
    @Override
    StringBuffer writeAttribute(StringBuffer buffer, 
            String prefix, ObjectName mbean,
            MBeanAttributeInfo info, Object value) {
        if (THREAD_MXBEAN_NAME.equals(mbean) && 
                info.getName().equals("AllThreadIds")) {
            
            // Instead of displaying only the thread ids, we will
            // display the thread infos.
            final Object threadInfos;
            
            try {
                threadInfos = server.invoke(mbean,"getThreadInfo",
                        new Object[] { value, 1 }, 
                        new String[] { long[].class.getName(),
                            int.class.getName() });
            } catch (Exception ex) {
                throw new IllegalArgumentException(mbean.toString(),ex);
            }
            buffer.append(prefix).append("# ").append("AllThreadInfo")
                .append("\\n");
            return dataDisplay.write(buffer,prefix,"AllThreadInfo",
                    threadInfos);
        } else {
            // default display.
            return super.writeAttribute(buffer,prefix,mbean,info,value);
        }
    }

}
Comments:

very very nice blog now i'm gona adopt your code to show graph in a new swing application

Posted by nizar on April 19, 2009 at 12:20 PM CEST #

can i get your code please and integrate it into my projecT?

Posted by nizar on April 19, 2009 at 12:24 PM CEST #

very useful article!

This is very similar to what you would get out of the jconsole mbeans tab. I was wondering if you could guide me on how to get general memory usage and thread information (such as what is displayed on the memory/thread tabs in jconsole)

Posted by Jeff on November 12, 2009 at 02:12 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Daniel Fuchs blogs on Scene Builder, JMX, SNMP, Java, etc...

The views expressed on this blog are those of the author and do not necessarily reflect the views of Oracle.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today