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.
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.
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() { mbs=ManagementFactory.getPlatformMBeanServer();
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); t.start();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:
try { ObjectName myname=new ObjectNameOn registration, reflection is used to identify the matching
("WatchMeBean:name=watchme"); mbs.registerMBean(wmb,myname); } catch (Exception e) { e.printStackTrace(); }
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.