Using the Platform MBean Server and Platform MBeans

Using the Platform MBean Server

An MBean server is a repository of MBeans that provides management applications access to MBeans. Applications do not access MBeans directly, but instead access them through the MBean server with their unique ObjectName. An MBean server implements the interface javax.management.MBeanServer.

J2SE 5.0 introduces the platform MBean server, an MBean server built-in to the JVM, which can be shared by all managed components running in the JVM. You access the platform MBean server with the method ManagementFactory.getPlatformMBeanServer().  Of course, you can also create your own MBean server using the MBeanServerFactory class.  However, there is generally no need for more than one MBean server, so using the platform MBean server is recommended.

Accessing Platform MBeans

Platform MBeans (MXBeans) were introduced previously.  A management application can access platform MBeans three different ways:

Using ManagementFactory

An application can directly call the methods of a platform MBean running in the same Java virtual machine.  To do so, use the static methods of the ManagementFactory class.  ManagementFactory  has accessor methods for each platform MBean such as getClassLoadingMXBean(), getGarbageCollectorMXBeans(), getRuntimeMXBean(), and so on.  In the cases where there are more than one platform MBean, the method returns a List of platform MBeans.

For example, the following code uses the static method of ManagementFactory to get the platform MBean RuntimeMXBean, and then gets the vendor name from the platform MBean:

RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
String vendor = mxbean.getVmVendor();

Using an MXBean Proxy

An application can also call platform MBean methods through an MXBean proxy. To do so, construct an MXBean proxy instance that forwards the method calls to a given MBeanServer by calling the static method ManagementFactory.newPlatformMXBeanProxy(). An application typically constructs a proxy to remotely access a platform MBean of another JVM.

For example, the following code does the same thing as the previous example, but using an MXBean proxy:

MBeanServerConnection mbs;
...
// Get a MBean proxy for RuntimeMXBean interface
RuntimeMXBean proxy =
ManagementFactory.newPlatformMXBeanProxy(mbs,
ManagementFactory.RUNTIME_MXBEAN_NAME,
RuntimeMXBean.class);
// Get standard attribute "VmVendor"
String vendor = proxy.getVmVendor();

Using MBeanServerConnection

An application can indirectly call platform MBean methods through an MBeanServerConnection connecting to the platform MBeanServer of a running JVM. You use the getAttribute() method of MBeanServerConnection to get an attribute of a platform MBean, providing the MBean's ObjectName and the attribute name as parameters.

For example, the following code does the same thing as the previous two examples, but uses an indirect call through MBeanServerConnection:

MBeanServerConnection mbs;
...
try {
ObjectName oname = new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);
// Get standard attribute "VmVendor"
String vendor = (String) mbs.getAttribute(oname, "VmVendor");
} catch (....) {
// Catch the exceptions thrown by ObjectName constructor
// and MBeanServer.getAttribute method
...
}

Using Sun's Platform Extension

JVM implementations may extend the management interface by defining interfaces for platform-specific metrics and management operations. The static factory methods in the ManagementFactory class will return the MBeans with the platform extension.

The com.sun.management package contains Sun Microsystems' platform extensions.  The following examples show how to access a platform-specific attribute from Sun's implementation of the RuntimeMXBean.

Direct Access

This example illustrates direct access to the Sun-specific MXBean interface:

com.sun.management.OperatingSystemMXBean mxbean =
(com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();

// Get the number of processors
int numProcessors = mxbean.getAvailableProcessors();

// Get the Sun-specific attribute Process CPU time
long cpuTime = mxbean.getProcessCpuTime();

Access Through MBeanServerConnection

This example illustrates access to the Sun-specific MXBean interface via MBeanServerConnection:

MBeanServerConnection mbs;

// Connect to a running JVM (or itself) and get MBeanServerConnection
// that has the JVM MXBeans registered in it
...

try {
// Assuming the OperatingSystem MXBean has been registered in mbs
ObjectName oname = new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME);

// Get standard attribute "OperatingSystem"
String vendor = (String) mbs.getAttribute(oname, "OperatingSystem");

// Check if this MXBean contains Sun's extension
if (mbs.isInstanceOf(oname, "com.sun.management.RuntimeMXBean")) {
// Get platform-specific attribute "ProcessCpuTime"
BarType bar = (String) mbs.getAttribute(oname, "ProcessCpuTime");
}
} catch (....) {
// Catch the exceptions thrown by ObjectName constructor
// and MBeanServer methods
...
}

Monitoring Thread Contention and CPU Time

The ThreadMXBean platform MBean provides support for monitoring thread contention and thread CPU time.

The Sun HotSpot JVM supports thread contention monitoring. Use the ThreadMXBean.isThreadContentionMonitoringSupported() method to determine if a JVM supports thread contention monitoring. Thread contention monitoring is disabled by default. Use the setThreadContentionMonitoringEnabled() method to enable it.

The Sun HotSpot JVM supports measuring thread CPU time on most platforms. The CPU time provided by this interface has nanosecond precision but not necessarily nanosecond accuracy.

Use the isThreadCpuTimeSupported() to determine if a JVM supports measuring of the CPU time for any thread and isCurrentThreadCpuTimeSupported() to determine if a JVM supports measuring of the CPU time for the current thread. A Java virtual machine implementation that supports CPU time measurement for any thread will also support that for the current thread.

A JVM may disable thread CPU time measurement. Use the isThreadCpuTimeEnabled() method to determine if thread CPU time measurement is enabled . Use the setThreadCpuTimeEnabled() method to enable/disable this thread CPU time measurement. 

Detecting Low Memory

Memory use is an important attribute of the memory system. It can indicate:

As described below, there are two kinds of memory thresholds you can use to detect low memory conditions: a usage threshold and a collection usage threshold.  You can detect low memory conditions using either of these thresholds with polling or threshold notification.

Note: Memory monitoring is intended for load-balancing or workload distribution: for example, an application could stop receiving new workload when its memory usage exceeds a certain threshold. Memory monitoring is not intended to enable an application to detect and recover from a low memory condition.

For more information, see the API reference for MemoryPoolMXBean.

Memory Thresholds

A memory pool may have two kinds of memory thresholds: a usage threshold and a collection usage threshold. Either one of these thresholds may not be supported by a particular memory pool.

Usage Threshold

The usage threshold is a manageable attribute of some memory pools.  It enables monitoring memory use with low overhead. Setting the threshold to a positive value enables usage threshold checking for a memory pool. Setting the usage threshold to zero disables usage threshold checking. The default value is supplied by the JVM.

A JVM performs usage threshold checking on a memory pool at the most appropriate time, typically during GC. Each memory pool increments a usage threshold count whenever the usage crosses the threshold.

Use the isUsageThresholdSupported()method to determine whether a memory pool supports a usage threshold, since a usage threshold is not appropriate for some memory pools. For example, in a generational garbage collector (such as the HotSpot VM), most of the objects are allocated in the young generation, from the "eden" memory pool. The eden pool is designed to be filled up; garbage collecting the eden memory pool will free most of its memory space since it is expected to contain mostly short-lived objects unreachable at garbage collection time. So, it is not appropriate for the eden memory pool to support a usage threshold.

Collection Usage Threshold

Collection usage threshold is a manageable attribute of some garbage-collected memory pools. After a JVM has performed garbage collection on a memory pool, some memory in the pool will still be in use. The collection usage threshold allows you to set a value for this memory. Use the isCollectionUsageThresholdSupported() method of MemoryPoolMXBean to determine if the pool supports a collection usage threshold.

A JVM may check the collection usage threshold on a memory pool when it performs GC. Set the collection usage threshold to a positive value to enable checking. Set the collection usage threshold to zero (the default) to disable checking.

Using Polling

An application can continuously monitor its memory usage by calling either the getUsage() method for all memory pools or the isUsageThresholdExceeded() method for memory pools that support a usage threshold.

The following example has a thread dedicated to task distribution and processing. At every interval, it determines if it should receive and process new tasks based on its memory usage. If the memory usage exceeds its usage threshold, it redistributes outstanding tasks to other VMs and stops receiving new tasks until the memory usage returns below the threshold.

pool.setUsageThreshold(myThreshold);
....
boolean lowMemory = false;
while (true) {
if (pool.isUsageThresholdExceeded()) {
lowMemory = true;
redistributeTasks(); // redistribute tasks to other VMs
stopReceivingTasks(); // stop receiving new tasks
} else {
if (lowMemory) { // resume receiving tasks
lowMemory = false;
resumeReceivingTasks();
}
// processing outstanding task
...
}
// sleep for sometime
try {
Thread.sleep(sometime);
} catch (InterruptedException e) {
...
}
}

The above example does not differentiate the case where the memory usage has temporarily dropped below the usage threshold from the case where the memory usage remains above the threshold between two iterations. You can use the usage threshold count returned by getUsageThresholdCount()to determine if the memory usage has returned below the threshold between two polls.

To test collection usage threshold instead, you would use the isCollectionUsageThresholdSupported(), isCollectionThresholdExceeded() and getCollectionUsageThreshold() methods similarly.

Using Threshold Notifications

When the  the MemoryMXBean detects that a memory pool has reached or exceeded its usage threshold, it emits a usage threshold exceeded notification. The MemoryMXBean will not issue another usage threshold exceeded notification until the usage has fallen below the threshold and then exceeded it again.  Similarly, when the memory usage after garbage collection exceeds the collection usage threshold, the MemoryMXBean emits a collection usage threshold exceeded notification.

The example code below implements the same logic as in the polling example, but uses usage threshold notification to detect low memory conditions. Upon receiving notification, the listener notifies another thread to perform actions such as to redistribute outstanding tasks, stop receiving tasks, or resume receiving tasks.

In general, design the handleNotification method to do a minimal amount of work, to avoid causing delay in delivering subsequent notifications. Perform time-consuming actions in a separate thread. Since multiple threads can concurrently invoke the notification listener, the listener should properly synchronize the tasks it performs.

class MyListener implements javax.management.NotificationListener {
public void handleNotification(Notification notification, Object handback) {
String notifType = notification.getType();
if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
// potential low memory, redistribute tasks to other VMs & stop receiving new tasks.
lowMemory = true;
notifyAnotherThread(lowMemory);
}
}
}

// Register MyListener with MemoryMXBean
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) mbean;
MyListener listener = new MyListener();
emitter.addNotificationListener(listener, null, null);

Assuming this memory pool supports a usage threshold, you can then set the threshold to some value (representing a number of bytes), above which the app will not accept new tasks.

pool.setUsageThreshold(myThreshold);

After this point, usage threshold detection is enabled and MyListener will handle notification.