JConsole, the essential J2SE tool

JConsole allows you to see inside your Java application while it's running. To do this you need to install the correct plumbing code, but we'll show you how.

JConsole allows you to see inside your Java application while it's running. To do this you need to install the correct plumbing code, but we'll show you how.

Keeping tabs on how your Java applications are performing got a whole lot easier in Java2 5.0 with the arrival of JConsole and JMX 1.2 as standard. JMX — Java Management Extensions — are nothing new; they appeared first as the result of JSR 3 back in 1999, aiming to create a universal framework for monitoring and managing Java applications.

When an application is run with JMX enabled, a JMX Agent process is activated within the JVM to manage JMX requests. JMX clients can then locate and connect to the JMX Agent and query it for the available management resources. JMX was later complemented with the JMX Remote API (JSR 160) which allowed for remote clients to access the JMX Agent. The JMX Remote API includes access control and SSL support which makes it secure enough for use in production.

JMX is one of those technologies that you'd only go and look at if you already knew you needed it, and as you usually had to craft your own management client, the usability curve was somewhat steep. That changed with the arrival of JConsole, a fully fledged JMX client, for plugging into the JMX framework.

Straight In
As a developer, you'll be wanting the quickest way to get started with JConsole. The first stage is to activate the JMX Agent on your application. Local connections are the simplest to configure and use. To make the JVM manageable, you add -Dcom.sun.management.jmxremote to your Java command line;

java -Dcom.sun.management.jmxremote WatchMe.jar

WatchMe is a simple example application we'll be using in this article, and we'll look inside it later.

In another window, run JConsole. You'll find that in the bin directory of your JAVA_HOME directory; so on Windows it would typically bec:\Program Files\Java\jdk1.5.0_02\bin\jconsole.exe. When JConsole runs it brings up the new connection window. There are three tabs, but we're only interested in the "Local" tab. All monitorable Java VMs are displayed with the process id; select WatchMe and connect to it. JConsole will now display its summary view.

JConsole Summary View

The Summary view gives you:

  • Times — Uptime, cpu time and compile time (that's time spent in the JIT compiler)
  • Threads — Counts of live threads, daemon threads, the number of threads started and the peak number of running threads
  • Memory — Heap sizes, objects awaiting finalisation and garbage collector statistics
  • Classes — Counts of the number of classes loaded and unloaded
  • Operating System — Amount of memory the available in the operating system, free memory and virtual memory

Threads, Memory and Classes have more in-depth data available through the tabs at the top of the window. Let's start with Memory;

On the Memory view, you can get a graph of memory usages, and view down into specific parts of the allocation system. The multiple level meters in the lower right are an at a glance view of how the various pools within that allocation system are being used. Note the "Perform GC" button which allows you to get the monitored application to do a garbage collection.

On the Threads View, you can see a graph of the thread usage over time. Below that is a list of live threads which you can select to get details of current execution state. If you have a lot of threads, the Filter field lets you cut down the displayed threads.

The Classes view is just a graph of the number of classes loaded, with a combobox to let you select the time range for the loading stats.

There are two more tabs in the JConsole dialog; the VM tab and the MBeans tab.

The VM tab gives details about the Java VM you are viewing, including class path, command line flags, host CPU type and other OS statistics.

Finally, there's the MBeans tab, and this is where the real power lies. MBeans are the core of the JMX framework, the route to managing and monitoring your own applications. For any class you want to to manage, you create a matching MBean which defines the management interface. In the WatchMe example, we have a class which needs monitoring, WatchMeBean.java. Now this is a simple class which has a counter which increments every second or whenever the incCount method is invoked. There's also a reset method which sets the count to zero. WatchMeBean also has a message string. What we would like to expose to management is the current value of the count, the message and the ability to reset the counter.

For this, we need to create an MBean and here it is:

public interface WatchMeBeanMBean 
	public int getCount();
	public String getMsg();
	public void setMsg(String msg);
	public void reset();
It is an interface, rather than a class, encapsulating the methods which we want JMX to consider as the management API. The MBean interface has to be named, case sensitively, as the name of the class to be managable with MBean appended, hence, to monitor WatchMeBean, the MBean is WatchMeBeanMBean; one to watch out for when you are refactoring an application with MBeans. A Simple MBean follows the classic JavaBean naming strategy of having get/set methods.

MBeans need to be registered with the MBeanServer; this is an Agent which handles the external clients connecting to the JMX framework. In WatchMe.java, we set this up;

	private MBeanServer mbs=null;
	public WatchMe()

The getPlatformMBeanServer() call creates and returns the MBeanServer which is also responsible for the JVM memory and thread information we saw earlier. Once we have the MBeanServer, we can register a class instance with it. First we create our WatchMeBean and start it in its own thread.

		wmb=new WatchMeBean();
		Thread t=new Thread(wmb);
Now, you may have many instances of the same class which you may want to manage. To discriminate between them, you create an ObjectName. This is made up of two parts, a domain and a list of attributes. Here we have a domain "WatchMeBean" and a name attribute set to "watchme". We'll see those names later. Once we have an ObjectName, we can register the bean with the MBeanServer:

			ObjectName myname=new ObjectName
("WatchMeBean:name=watchme"); mbs.registerMBean(wmb,myname); } catch (Exception e) { e.printStackTrace(); }
On registration, reflection is used to identify the matching WatchMeBeanMBean interface (which is why getting the MBean name right is important). And that's it. You now have a monitorable bean.

It is at this point we can return to JConsole and have a look under the MBeans tab.

The MBeans tab shows all the MBeans registered with the server. The first level children reflect the domain name set in the ObjectName when the MBeans were registered. JMImplementation contains MBeans for the JMX agent itself, "java.lang" contains the MBeans which provided all the VM statistics you see in the other tabs. You should also see WatchMeBean, the domain we registered WatchMeBean with. Within that is an MBean by the name of "watchme", from the name attribute in the ObjectName. When you select an MBean, the right hand side of the JConsole window shows four tabs. The first tab is the attributes view.

In the example we have the Count field. Because the WatchMeBeanMBean only has a get method for Count, this is a read only attribute. If you click on the value field for Count, you can get a dynamically updated graph of its value over time.

We also have the Msg field, which is highlighted in blue to show it's a read/write attribute; it has a get and a set method. You can click on the value field and change the message. This, through the MBeanServer and the MBean interface, calls the setMsg() method. Try changing it and you'll see it updated in the WatchMe window.

If you want to be sure you are looking at the right instance of the class, you can use the Info tab, which displays the name and class of the MBean you are looking at:

There is only one method left in the WatchMeBeanMBean unaccounted for; reset. You can find that under the Operations tab.

As it doesn't conform to a property getter/setter, the reset method is regarded as an operation, allowing you to expose management control methods to JConsole. The example reset method doesn't have any parameters, but JConsole can handle operations with parameters. If you click on the reset button, you'll see WatchMe's count drop back to 0.

So what we have with the combination of JMX and JConsole is a quick easy way to expose the internals of your application. JConsole isn't the only application that can access the MBeans within the application, you can write your own JMX clients after using JConsole to prototype and test the management functions in your application. JConsole can also connect to multiple applications simultaneously. Next month, we'll look at securely connecting to remote applications and the other kinds of MBeans you can use to make your management API more dynamic.

If you can't wait for that, check out Sun's JMX overview which has links out to JMX related resources.

Download the example code for this article.

DJ Walker-Morgan is a consulting developer, specialising in Java and user-to-user messaging and conferencing.