Level: Introductory May Glover Gunn (mglovergunn@uk.ibm.com), Software Engineer, IBM George Harley (gharley@uk.ibm.com), Software Developer, IBM Caroline Gough (goughc@uk.ibm.com), Software Engineer, IBM
11 Apr 2006 The latest release of the Java™ platform includes a number of new system monitoring and management features. In this article, three developers from the IBM Java Technology Centre team up to get you started using this API. After a quick overview of the java.lang.management package, they guide you through a number of short practical scenarios to probe the performance of a running JVM.
Among the many new features introduced in version 5.0 of the Java 2 platform is an API that enables Java applications and compliant tools to both monitor and manage a Java virtual machine (JVM) and the native operating system on which it runs. In this article, you will learn about capabilities of this new platform management API, which is housed in the java.lang.management package. This article gets you quickly up to speed with a new set of powerful features that will become more important in future Java platform releases.
Monitoring and managing a 5.0 VM
Java 5.0 provides new abilities to monitor and manage a running virtual machine. Developers and system administrators can oversee -- and, for some properties, fine-tune -- the performance of a 5.0 VM. The mechanism for carrying out these activities will be familiar to anyone who has prior experience of using Java Management Extensions (JMX). Through JMX technology, a set of platform resources to be controlled can be regarded as simple, well-defined objects whose properties map to the lower-level characteristics of the given resource.
In the platform management API, these well-defined objects are called MXBeans. If you think an MXBean sounds like a type of the possibly more familiar MBean, then you are correct. These MXBeans (or platform MBeans) are, in effect, management beans encapsulating specific parts of the 5.0 platform's internals. Figure 1 illustrates how MXBeans fit into the larger picture:
Figure 1. MXBeans provide management interfaces for the Java platform
You can discover and customise many different kinds of functionality in a running 5.0-compatible VM; for instance, you could get the details on the behaviour of the just-in-time compilation system that may be in use or obtain progress figures for the garbage collection service.
Any Java application can make use of platform beans by simply obtaining the necessary bean reference (using the techniques we'll outline in this article) and then invoking the appropriate method calls. In the simplest scenario, the bean client may discover information about the platform that the client itself is running on. But a client can also monitor the behaviour of a completely separate JVM. This is possible because an MXBean is a kind of MBean and may be remotely managed using the standard JMX services available in Java 5.0.
JConsole
One example of a bean client is the JConsole tool that ships with the Java SE 5.0 SDK. This is a graphical interface that connects to a JVM to show information about it. Tabs in the GUI relate to particular aspects of the JVM; there are Memory, Threads, and Classes tabs. The JConsole tool also provides an overall Summary tab, a VM tab that gives information on the environment in which the VM was started, and an MBeans tab with which the user can inspect the state of the platform MBeans in more detail.
Running JConsole
You can start JConsole by simply typing jconsole at a command prompt, assuming that the bin directory of the SDK is in your path. Enter the hostname where the JVM you want to monitor is running together with the port number on which it is listening for management requests -- along with any authentication details needed -- and then click Connect. Clicking Connect with the default values of localhost and port 0 monitors the JVM being used to run JConsole itself (because JConsole is a Java process). This is known as self-monitoring mode. Figure 2 illustrates JConsole starting up:
Figure 2. JConsole starting up
JConsole in action
Once a connection to a JVM has been established, JConsole starts up with the Summary tab, as shown in Figure 3:
Figure 3. JConsole's Summary tab
From here, you can select any of the other tabs. For example, the Memory tab, illustrated in Figure 4, shows a history of the usage of each memory pool in the JVM:
Figure 4. JConsole's Memory tab
Notice the Perform GC button in the top-right corner of the panel. This is an example of one of the many operations that you can invoke on the JVM using the platform MBeans.
How does that work?
Underpinning everything you have read so far is the concept of a managed bean, or MBean. You can think of MBeans as being the programmatic representation of the management interface of a resource. In simpler terms, you can think of them as Java wrappers around an entity to be controlled. In more practical terms still, MBeans are Java classes whose public methods are written according to a well-defined set of rules; these rules enforce complete encapsulation of those characteristics of an application or resource to be managed. Ultimately, the manager of the resource (whatever it may be and wherever it may live on the network) locates and uses the corresponding MBean for the purposes of control.
Through its API, an MBean provides information about the following, all of which Figure 5 illustrates:
- The current state of the resource, through its properties
- Operations that can be invoked on it by management agents
- Possible event notifications that could be sent to interested parties
Figure 5. MBean clients make use of properties, operations, and events
Once you create it, you need to register an MBean with an MBean server. In addition to playing the role of an MBean registry, the MBean server also provides a way for management systems to find and utilise registered MBeans. Additional functionality for managing registered MBeans is performed by JMX agent services. Such services include monitoring attribute values of an MBean and notifying interested parties about changes to them, notifying listeners periodically with specific information about an MBean, and policing the relationships between MBeans. JMX agent services are often MBeans themselves.
The combination of an MBean server with the mandatory JMX agent services is referred to as a JMX agent, shown in Figure 6:
Figure 6. The JMX agent
A JMX agent can make its managed resources -- that is, the set of MBeans currently registered in its MBean server --- available to other remote agents.
Prior to the release of Java 5.0, the javax.management API was an optional extension to the Java platform that users could obtain in a separate download and put to use as a means of managing and monitoring resources through Java code. Resources in this context can mean applications; J2EE servers running business-critical applications; plain old Java objects (POJOs); and even hardware entities, such as network devices, set-top boxes, telecommunications equipment, or the like. If you can reference it from Java code, then it can potentially become a managed resource.
Although we have really only scratched the surface of JMX here, we have covered enough to allow us to look at MXBeans. A comprehensive discussion of JMX's design and capabilities is beyond the scope of this article. For an excellent overview of the part JMX plays in network management applications, you can read Sing Li's series on the subject (see Resources).
What are MXBeans and how can I use them?
So now that you known what MBeans are, let's take a look at their near-namesakes as defined in the java.lang.management package, MXBeans. The good news is that MXBeans do not deviate from the concepts we introduced when discussing MBeans. Most of the types in this package are interfaces that follow a naming convention similar to that used for standard MBeans: the name of the platform resource being instrumented followed by the suffix MXBean. (For standard MBeans the MBean suffix would be expected, of course.)
Table 1 outlines the platform resources that are available for instrumentation through the MXBean interfaces provided in the java.lang.management package:
Table 1. Platform resources that can be managed through MXBeans
| Platform resource | Corresponding MXBean | Number available |
|---|
| Compilation | CompilationMXBean | 0 or 1 | | Garbage collection system | GarbageCollectorMXBean | At least 1 | | Memory | MemoryMXBean | Exactly 1 | | Memory managers | MemoryManagerMXBean | At least 1 | | Threading | ThreadMXBean | Exactly 1 | | Operating system | OperatingSystemMXBean | Exactly 1 | | Runtime system | RuntimeMXBean | Exactly 1 | | Class loading system | ClassLoadingMXBean | Exactly 1 | | Memory resources | MemoryPoolMXBean | At least 1 |
For each MXBean, the interface to which clients must program is firmly set in the Java 5.0 specification. There is currently no way that users can customise an interface such that it exposes any more manageable attributes of the platform.
The number of possible instances of each MXBean type, indicated in the third column of Table 1, depends very much on the particular platform system being managed. For example, as the JVM specification allows implementers to choose the garbage collection algorithms used, it is entirely reasonable for any number of garbage collectors -- and therefore any number of GarbageCollectionMXBean instances -- to be active at any one time. Contrast this with the OperatingSystemMXBean, of which there can only be one instance available, as the instrumented virtual machine can obviously only be running on one operating system at any given time.
Client code can safely assume that the one-off MXBeans are true singletons in the VM. Any time a reference is requested to these one-off types, it is always answered with the same instance, regardless of where the reference request is made from and when it occurs in the lifecycle of the VM. This holds true even when multiple clients are monitoring a particular VM.
 |
The MBeanServerConnection
The interface javax.management.MBeanServerConnection is a supertype of the javax.management.MBeanServer interface that may be used to call into the MBean server if it is running in the same VM as the client code (that is, if the management client and JMX agent are co-located in the same VM). Because there is a parent-child relationship between MBeanServerConnection and MBeanServer, clients can use the same method calls to interact with either remote or local MBean servers.
|
|
To Java client code, an instance of an MXBean behaves like any POJO. Information can be acquired through a direct call to the object without the need for any other participants. Of course, this is only the case so long as the Java client has obtained a reference to a local bean (running on the same VM as the management application) directly or else has requested a proxy to a bean encapsulating part of a remote VM. In both cases, the reference is obtained from the platform's singleton ManagementFactory.
You can also access a platform bean through the javax.management.MBeanServerConnection, but, in such a case, there is an extra level of indirection in the conversation. As you will see in Monitoring a remote VM through the platform server, in that scenario clients always make requests to an MBeanServerConnection to locate and make calls on their behalf to the specified remote beans. This is in keeping with the JMX outline given earlier, where we noted that, to issue calls on any remote MBean, remote clients must communicate with the MBean server with which the MBean is registered.
MXBeans are not JavaBeans
To avoid any confusion, you should remember that while it is perfectly all right to think of an MXBean as a kind of MBean that helps monitor and control a JVM, it is most certainly not correct to think of an MXBean as a type of JavaBean. JavaBeans technology is the component model for the Java platform that was designed to offer the ability to construct applications from reusable Java components using graphical tooling. While some of the features of JavaBeans, such as meaningful naming conventions to aid the discovery of properties by tools, are present in the MBean and MXBean space, there is a world of difference between the two technologies. Don't confuse them.
The extra MXBean
At the beginning of this article, we mentioned that the java.lang.management package houses the platform management API. Well, here we need to amend that statement slightly because not all MXBeans are actually contained in that package. Because the LoggingMXBean is so intrinsically tied to the Java platform's logging functionality, it made more sense to house it in the java.util.logging package. As you would expect from its name, this type of MXBean provides a management interface for a running VM's logging facility. From a reference to this bean, clients can obtain the names of all of the loggers that have been registered with the platform and their relationships with one another. Also available is the ability to get and set the level of a named platform logger.
Like the OperatingSystemMXBean and the ThreadMXBean (to name two other examples), the LoggingMXBean exists as a singleton in the running VM. Any gets or sets on its exposed properties, through any means of communication, are always routed to the same object instance.
Getting MXBeans
There are three ways for client code to access MXBeans: through factory methods, through the platform server, or as proxies.
Factory methods
The simplest way to retrieve MXBeans is to use the static methods provided in the java.lang.management.ManagementFactory class. However, MXBeans obtained in this way can only be used to monitor your local VM. The ManagementFactory class defines one retrieval method for each type of MXBean. Some of these methods return a single instance of the MXBean, and others return a strongly typed List of MXBean instances.
When there is only one MXBean of a specific type, the code to retrieve it is simple. Listing 1 shows the code to retrieve the ThreadMXBean:
Listing 1. Retrieving a reference to the platform's sole ThreadMXBean
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
For those MXBean types where multiple instances of the MXBean may exist, there are factory methods that return the MXBeans in a List, as shown in Listing 2:
Listing 2. Retrieving a strongly typed list of all MemoryPoolMXBeans known to the platform
List<MemoryPoolMXBean> memPoolBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean mpb : memPoolBeans) {
System.out.println("Memory Pool: " + mpb.getName());
}
|
The LoggingMXBean is part of the java.util.logging package and as a result, it is accessed using the LogManager class rather than ManagementFactory class, as shown in Listing 3:
Listing 3. Get the LoggingMXBean reference from the LogManager
LoggingMXBean logBean = LogManager.getLoggingMXBean();
|
Remember, these methods only allow you to access the MXBeans belonging to your local VM. If you want to extend the reach of your client code to instrument remote JVMs located on the same machine or on different nodes altogether, then you need to use one of the two methods we'll describe next.
Through the platform server
Structuring your code to make calls on a connection to a remote VM's MBean server is one choice available to you. For this to succeed, you would first need to launch the remote VM with key command-line options. These set the port on which its associated JMX agent listens for requests and the level of security that will be in effect. For example, the following options start a VM whose agent listens on port 1234 with no security:
-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
|
Allowing secure access to a VM is covered in the Security section later in this article.
With the remote agent listening, you could use the code snippet in Listing 4 to obtain a reference to the associated MBean server connection:
Listing 4. Using the JMXConnectorFactory to connect to a different VM's MBean server
try {
// connect to a separate VM's MBeanServer, using the JMX RMI functionality
JMXServiceURL address =
new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://localhost:1234/jmxrmi");
JMXConnector connector = JMXConnectorFactory.connect(address);
MBeanServerConnection mbs = connector.getMBeanServerConnection();
} catch ...
|
Once you have retrieved the MBeanServerConnection, you can use the JMX methods getAttribute(), setAttribute(), and invoke() to work with the MXBeans. This is covered in Monitoring a remote VM through the platform server.
As proxies
The third approach to accessing the platform beans API shares some similarities with both methods already introduced. As before, you need to retrieve the MBeanServerConnection to the JMX agent of the VM you wish to monitor. Then, by using a static helper method on the ManagementFactory class, the client code can request a proxy instance to a named MXBean registered with the MBean server of the remote VM. Listing 5 illustrates an example:
Listing 5. A reference to a remote MBean server can obtain proxies to remote MXBeans
try {
ThreadMXBean threadBean = ManagementFactory.newPlatformMXBeanProxy
(mbs, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class);
} catch ...
|
For all singleton MXBeans except the LoggingMXBean, the complete string names used for server registration are available in public static fields of the ManagementFactory class. So, for instance, the string representation of the javax.management.ObjectName for ThreadMXBean is stored in the THREAD_MXBEAN_NAME field. Likewise, the registration name for the LoggingMXBean is stored in a static field in the java.util.logging.LogManager class. Listing 6 demonstrates a request for a proxy instance for the LoggingMXBean:
Listing 6. The string name of the LoggingMXBean is a constant of the java.util.logging.LogManager class
try {
LoggingMXBean logBean = ManagementFactory.newPlatformMXBeanProxy
(mbs, LogManager.LOGGING_MXBEAN_NAME, LoggingMXBean.class);
} catch ...
|
With MXBean types where more than one instance can exist in the VM, things become slightly more involved. In such cases, you first need to use the MBeanServerConnection to obtain the names of all the registered MXBeans of a given type. As a convenience, the domain part of the ObjectName for each non-singleton MXBean is stored in public static fields in ManagementFactory. Once the names are retrieved, each one can then be used to construct a separate proxy instance. Listing 7 shows an example:
Listing 7. Creating proxies for each MemoryManagerMXBean belonging to a remote VM
try {
// Get the names of all the Memory Manager MXBeans in the server
Set srvMemMgrNames = mbs.queryNames(new ObjectName(
ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + ",*"), null);
// Get a MXBean Proxy for each name returned
for (Object memMgrName : srvMemMgrNames){
// Cast Object to an ObjectName
ObjectName memMgr = (ObjectName) memMgrName;
// Call newPlatformMXBeanProxy with the complete object name
// for the specific MXBean
MemoryManagerMXBean memMgrBean =
ManagementFactory.newPlatformMXBeanProxy(
mbs, memMgr.toString(), MemoryManagerMXBean.class);
// memMgrBean is a proxy to the remote MXBean. We can use it
// just as if it was a reference to a local MXBean.
System.out.println("Memory Manager Name = " +
memMgrBean.getName());
}
} catch ...
|
Using MXBeans
The operations defined by each MXBean interface are listed in the java.lang.management documentation. Through these operations, the user can manage and monitor the virtual machine. For example, on the MemoryMXBean, there are operations that allow you to enable verbose output for the memory system, request garbage collection, and retrieve detailed information about the current memory usage by both heap and non-heap memory pools. So, if you were concerned about the amount of memory a Java application was using or wished to tune the heap size, you could easily write a management client using the java.lang.management API to connect to the application and monitor the memory usage.
Similarly, ThreadMXBean offers functionality that could be useful if your Java application hangs. The findMonitorDeadlockedThreads() method returns the IDs of any threads it identifies as deadlocked. You could then use these IDs to retrieve detailed information about the threads, including their stack traces, their states, whether they were executing native code, and so on.
This thread information is supplied in an instance of the ThreadInfo class, one of three classes in the java.lang management package used by MXBeans to return snapshots of data to a user -- the others being the MemoryUsage and MemoryNotificationInfo classes. Each of these is a complex data type containing structured information describing specific platform qualities.
Now let's look at two example scenarios that illustrate how the ideas discussed above translate into Java code.
Example 1: Monitoring a VM through MXBeans or proxies
As we discussed, an MXBean's methods may either be called directly on a local MXBean or else through a proxy. Listing 8 shows how to use the ThreadMXBean's getter and setter operations. The threadBean variable in this example can either be an MXBean retrieved from the local VM or a proxy to the MXBean from a remote VM. It is transparent to the caller once the reference has been acquired.
Listing 8. Getting and setting the values of a ThreadMXBean
try {
// Get the current thread count for the JVM
int threadCount = threadBean.getThreadCount();
System.out.println(" Thread Count = " + threadCount);
// enable the thread CPU time
threadBean.setThreadCpuTimeEnabled(true);
} catch ...
|
The setThreadCpuTimeEnabled() method used in Listing 8 is optionally supported by 5.0-compatible VMs. The check required when using such optional functionality is shown in Listing 9:
Listing 9. Checking that an optional property is supported before attempting to use it
if (threadBean.isThreadCpuTimeSupported()) {
threadBean.setThreadCpuTimeEnabled(true);
}
|
The getTotalCompilationTime() method of the CompilationMXBean type also contains functionality that won't necessarily be available in every 5.0-compatible VM implementation. As with setThreadCpuTimeEnabled() in Listing 9, there is an associated method available for checking whether support exists. Code that does not take advantage of these checking methods needs to handle any java.lang.UnsupportedOperationExceptions that may be thrown by optional methods.
Listing 10 demonstrates how to access information about all the threads running in the VM. Each thread's information is stored in a separate dedicated ThreadInfo object, which can be subsequently queried.
Listing 10. Getting the names of all threads running in a VM
try {
// Get the ids of all the existing threads
long[] threadIDs = threadBean.getAllThreadIds();
// Get the ThreadInfo object for each threadID
ThreadInfo[] threadDataset = threadBean.getThreadInfo(threadIDs);
for (ThreadInfo threadData : threadDataset) {
if (threadData != null) {
System.out.println(threadData.getThreadName());
}
}
} catch ...
|
Keep in mind that the information contained in complex types like ThreadInfo, MemoryUsage, and MemoryNotificationInfo is purely a snapshot of the system at the moment that the acquiring call was made. These objects are not dynamically updated after you obtain a reference to them, so, if your application needs fresh data on one of these aspects of the managed VM, you need to make another call to get an updated ThreadInfo or MemoryUsage object. MemoryNotificationInfo objects are a bit different in this respect, as they aren't pulled by management applications but are rather pushed in notification events (which we discuss in more detail shortly).
Example 2: Monitoring a remote VM through the platform server
Accessing the ThreadMXBean of a remote JVM using an MBeanServerConnection is not quite as straightforward as the example given in Listing 1. First, you'll need a javax.management.ObjectName instance for the ThreadMXBean. You can create this with the same name string as an MXBean proxy object, as shown in Listing 11:
Listing 11. Constructing an ObjectName for a ThreadMXBean
try {
ObjectName srvThrdName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
...
} catch ...
|
You can use the ObjectName instance to identify the specific remote ThreadMXBean in subsequent calls to theMBeanServerConnection's getAttribute(), setAttribute(), and invoke() methods, as shown in Listing 12:
Listing 12. Putting the ObjectName to good use in calls to the remote MBean server
try {
// Get the current thread count for the JVM
int threadCount =
((Integer)mbs.getAttribute( srvThrdName, "ThreadCount")).intValue();
System.out.println(" Thread Count = " + threadCount);
boolean supported =
((Boolean)mbs.getAttribute(srvThrdName, "ThreadCpuTimeSupported")).booleanValue();
if (supported) {
mbs.setAttribute(srvThrdName,
new Attribute("ThreadCpuTimeEnabled", Boolean.TRUE));
...
}
} catch ...
|
Listing 13 demonstrates how to access the information about all current threads in the VM through the MBean server connection. MXBeans accessed using this approach return complex data types by wrapping the complex type inside a JMX open type, such as a javax.management.openmbean.CompositeData object.
Why is complex data wrapped inside an intermediate type? Keep in mind that MXBeans could potentially be managed by remote applications that are not actually written in the Java language or by Java applications without access to all of the complex types used to describe various qualities of the managed resources. While it is safe to assume that both ends of the connection to the platform JMX agent will understand simple types such as boolean, long, and string and will map them to the corresponding types in their respective implementation language, it is not realistic to assume that complex types such as ThreadInfo or MemoryUsage will be correctly interpreted by every possible management application. Open types like CompositeData can represent complex (that is, non-primitive or structured) data in terms of the more fundamental types.
If you make a remote call to a 5.0 MXBean that requires passing an instance of a complex type, the object is converted to its CompositeData equivalent. While this enables information to be sent to the widest possible range of clients, there is a downside: Receiving Java apps that actually can resolve the ThreadInfo and MemoryUsage types will nevertheless still need to convert from the open type to the complex type themselves. But even that is not too onerous a step, as all of the supporting complex data types defined in java.lang.management have their own static convenience methods for doing this.
In Listing 13, the threadDataset attribute contains an array of CompositeData objects that map directly to ThreadInfo objects. For each thread, the ThreadInfo's static method from() is then used to construct an equivalent ThreadInfo object from the CompositeData. You can use this object to access information about each thread.
Listing 13. The CompositeData type transfers complex data structures around the network
try {
// Get the ids of all the existing threads
long[] threadIDs = (long[])mbs.getAttribute(srvThrdName, "AllThreadIds");
// Get the ThreadInfo object for each threadID. To do this we need to
// invoke the getThreadInfo method on the remote thread bean. To do
// that we need to pass the name of the method to run together with the
// argument and the argument type. It's pretty ugly we know.
CompositeData[] threadDataset =
(CompositeData[]) (mbs.invoke(srvThrdName, "getThreadInfo",
new Object[]{threadIDs}, new String[] {"[J"}));
// Recover the ThreadInfo object from each received CompositeData using
// the static helper from() method and then use it to print out the
// thread name.
for (CompositeData threadCD : threadDataset) {
ThreadInfo threadData = ThreadInfo.from(threadCD);
if (threadData != null) {
System.out.println(threadData.getThreadName());
}
}
} catch ...
|
API support
If you develop platform management code that checks on the state of VM threads, then you may encounter some interesting behaviour that we chanced upon while putting this article together. Will it affect your code? That depends on how much your application uses version 5.0 of the Java platform's new mechanism for safeguarding code blocks from concurrent access.
The new 5.0 package java.util.concurrent.locks introduces the ReentrantLock class, which, as its name suggests, you can use to construct a reentrant lock for protecting critical sections of code. It's very similar to the long-established implicit locking mechanism that you get with the synchronized keyword, but it has some extra capabilities that can be useful for more fine-grained control of the explicit lock. Listing 14 shows an example of its use:
Listing 14. A very simple use of ReentrantLock
private Lock myLock = new ReentrantLock();
...
void myMethod() {
// Acquire the lock
myLock.lock();
try {
... do work in critical section ...
} finally {
// Relinquish the lock
myLock.unlock();
}// end finally
...
}
|
Before entering the critical section, the lock() method of the ReentrantLock object is invoked to try and obtain the lock. This only succeeds if another thread does not already have the lock, in which case the current thread is blocked. Prior to version 5.0 of the Java platform, you might have written the functionality in Listing 14 in code something like that in Listing 15. (You still can, of course, as synchronized hasn't gone away.)
Listing 15. A synchronized method
synchronized void myMethod() {
... do work in critical section ...
}
|
In these simple uses, you won't see a difference in the behaviour of the code. If, however, you make use of the ThreadMXBean and ThreadInfo types to check on the blocked count of a thread that you know will be blocked from entering a critical section in the course of a running program, the result will differ depending on the locking method used. You can demonstrate this for yourself by writing some simple code that has two different threads attempt to call into myMethod() and force one thread to always arrive there second. That thread will obviously be blocked and should have a blocked count of exactly one. Using the synchronized keyword with myMethod() you will, as expected, see a blocked count greater than zero from the ThreadInfo object associated with the thread. However, using the new ReentrantLock approach, you will see a blocked count of zero. It is a safe bet that, as the VM monitoring capabilities of ThreadMXBean catch up with the new concurrent packages, this difference in observed behaviour will be removed in future Java platform releases.
 |
Notifications
The MemoryMXBean is unique among MXBeans in that it has the capability to send dynamic notifications of interesting events concerning memory usage to client objects. The benefits of immediate communication on such matters as memory usage exceeding some preset threshold should be obvious, as it may be a symptom of problems at the application level or else indicate that further tuning of the virtual machine is required.
The notification model used by the MemoryMXBean comes from the JMX MBean specification, which in turn is very similar to the events notification model used in Java programming. As a broadcaster of notifications, the MemoryMXBean implements the JMX interface javax.management.NotificationBroadcaster, a relatively small interface that enables the bean to both register and unregister interested parties. In turn, each interested party (object) must implement the javax.management.NotificationListener interface. This interface consists of just one operation, which is invoked by the event emitting an MXBean when an event occurs.
Listeners can be registered (or unregistered) with the MemoryMXBean at any time during the VM life cycle. Notifications are only broadcast to the current set of registrants.
When a listener's handler method is called, it receives an instance of the javax.management.Notification class. This is the generic event signal type in the JMX events notification model. Its design is such that it can be set to hold a substantial amount of information on the event that led to its creation. For the MemoryMXBean, there are currently two types of notifications:
- A memory resource (sometimes called a memory pool) in the VM has grown to exceed a previously set threshold value. This kind of event is identified by the
MemoryNotificationInfo constant MEMORY_THRESHOLD_EXCEEDED.
- The size of a memory resource immediately after garbage collection is greater than a previously set threshold value. This is identified by the
MemoryNotificationInfo constant MEMORY_COLLECTION_THRESHOLD_EXCEEDED.
Upon receiving a Notification object in its handler method, a registered listener can determine the kind of event that has occurred by querying the type of the Notification and checking the resulting string against the two MemoryNotificationInfo values.
To pass detailed information on the event to listeners, the MemoryMXBean sets each emitted Notification object's user data property (essentially, a means for the broadcaster to include anything it wants) with a concrete instance of javax.management.openmbean.CompositeData representing a MemoryNotificationInfo object. As we explained in Monitoring a remote VM through the platform server, encapsulating the event data inside a JMX open type enables it to be understood by the broadest set of listeners.
Security
So far, so good. Now it's time to confront that elephant in the corner: security. What if you don't want someone's application code to be able to access and modify your VM? What options are available? There are some system properties that you can set to control the level of access and the way in which VM data is transmitted from the JMX agent to the management client. These fall into two categories: password authentication and Secure Sockets Layer (SSL).
Using command-line options
To make a 5.0-compatible VM accessible to monitoring and management, you need to set up a port number for the platform's JMX agent using the following command-line option:
-Dcom.sun.management.jmxremote.port=<number>
|
In the unlikely situation that you don't care who can access your VM through this port, you can also switch off password authentication and SSL encryption (both of which are enabled by default) by adding the following two options:
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
|
Using these three options together is convenient when you're developing your java.lang.management client code and want to be able to monitor another VM easily. In a production environment, you need to set up either password control or SSL (and perhaps both).
Password authentication
In the jre/lib/management directory of your 5.0 SDK, you'll find a file called jmxremote.password.template. This defines usernames and passwords for two roles. The first is a monitor role, allowing access to read-only management functions; the second role is a control role for read-write functions. Clients can authenticate with either the monitorRole or controlRole usernames, depending on the level of access they require. To make sure that only these authenticated users can have access, you need to do the following:
- Copy the contents of jmxremote.password.template to a file called jmxremote.password and uncomment the lines at the end of the file that define the usernames and passwords, changing the passwords as desired.
- Modify the permissions on jmxremote.password so that it can only be read and modified by the owner. (On UNIX and UNIX-like systems, set the permissions to 600. On Microsoft Windows, follow the instructions in the article "How to secure a password file on Microsoft Windows systems," which you can find a link to in Resources.)
- When starting up the VM, specify the location of the password file to use with the following command-line option:
-Dcom.sun.management.jmxremote.password.file=<file-path>
|
From the perspective of the management client, you need to provide a valid username/password combination to access a VM that has authentication enabled. If the client is JConsole, this is simple: the username and password fields are provided in the initial Connection tab. To write code that provides authentication details to a remote VM, you need to add the modifications shown in Listing 16 to the connection code previously given in Listing 4:
Listing 16. Connecting to a remote VM that demands user authentication
try {
// provide a valid username and password (e.g., via program arguments)
String user = "monitorRole";
String pw = "password";
// place the username and password in a string array of credentials that
// can be used when making the connection to the remote JMX agent
String[] credentials = new String[] { user, pw };
// the string array of credentials is placed in a map keyed against the
// well-defined credentials identifier string
Map<String, String[]> props = new HashMap<String, String[]>();
props.put("jmx.remote.credentials", credentials);
// supply the map of credentials to the connect call
JMXServiceURL address =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1234/jmxrmi");
JMXConnector connector = JMXConnectorFactory.connect(address, props);
// it is a trivial matter to get a reference for the MBean server
// connection to the remote agent
MBeanServerConnection mbs = connector.getMBeanServerConnection();
} catch ...
|
Providing an invalid username or password to a VM that requires authentication results in a java.lang.SecurityException. Similarly, authenticating as the monitorRole and then attempting to invoke one of the read-write operations -- like trying to request garbage collection -- also causes a SecurityException to be thrown.
Using SSL
You can use SSL to encrypt the information that passes from a platform's JMX agent to the management client monitoring it. The transmitted data is encrypted using public key (asymmetric) cryptography so that only the holder of the corresponding private key can decrypt the data. This prevents packet sniffing applications from eavesdropping on the communication. Making use of this feature requires that both ends of the connection have SSL configured, which involves generating a pair of keys and a digital certificate. The details are beyond the scope of this article; please read Greg Travis' excellent tutorial ,"Using JSSE for secure socket communication," to find out more (see Resources).
The good news is that once the keypair and certificate have been set up, you don't need to modify your management client code to make use of SSL. You can enable the encryption just by using certain command-line options. Firstly, the Java application you want to monitor or manage must be started with the following options:
-Dcom.sun.management.jmxremote.ssl.need.client.auth=true
-Djavax.net.ssl.keyStore=<keystore-location>
-Djavax.net.ssl.trustStore=<truststore-location>
-Djavax.net.ssl.keyStoreType=<keystore-type>
-Djavax.net.ssl.keyStorePassword=<keystore-password>
-Djavax.net.ssl.trustStoreType=<truststore-type>
-Djavax.net.ssl.trustStorePassword=<truststore-password>
|
Management clients wishing to communicate with the platform need to be launched with a subset of the above options: You can omit the first and the final two lines. If your client is JConsole, you can pass these options in when you start the GUI using the -J command-line syntax, which passes Java options through to the JVM.
Again, the tutorial "Using JSSE for secure socket communication" can provide more detail on these individual options.
Conclusion
We hope we have spurred you to find out more about the Java 5.0 platform's management API. Just as the advent of Java Management Extensions provided Java enterprise developers and administrators with a standardised way to monitor and control their deployments, so the introduction of the java.lang.management API in Java 5.0 gives you the means to peer under the hood of the very platform on which your applications run.
MXBeans are pretty straightforward to both obtain and use, whether you are keeping tabs on thread pools in an application running locally or securely checking the memory usage of a mission-critical program in need of attention elsewhere on your intranet. MXBeans have a central part to play in helping you understand more about the world that your code runs in, whether you're probing and trying to understand the characteristics of unfamiliar Java implementations in a non-intrusive way or building your own profiling and performance monitoring tooling.
As "diagnosability, monitoring, and management" is one of the key themes for the upcoming 6.0 release of the Java platform, this API is set to take on an increasingly important role in the future of Java technology.
Resources Learn
Get products and technologies
- MBeanInspector: WebSphere 5 users can download this technology to explore MBeans registered with the application server.
Discuss
About the authors  | |  | May Glover Gunn is a physicist by training, but joined IBM on a whim three years ago. Since then, she has worked in the Java Technology Centre at the IBM Hursley Laboratory in a variety of roles. Recently, she developed tests for the 5.0 Java technology release and is currently working on new technologies involving the Java platform. |
 | |  | George Harley is a senior developer at the IBM Hursley Laboratory working in the Java Technology Centre. He has spent over a decade writing software professionally in a variety of languages. In his spare time he works. |
 | |  | Caroline Gough worked as a developer in a small software house for three years before joining the Java Technology Centre System Test team at the IBM Hursley Laboratory. She is a senior tester with expertise in stress testing and RAS (reliability, availability, and serviceability) tooling. She worked on the recent port of version 5.0 of the Java platform from IBM and is now preparing tests for future Java platform releases. |
Rate this page
|