The following class demonstrates how to connect to a running JVM and establish a JMX connection, loading the JMX agent if necessary. It will print System Properties (this works through the JVM connection without the need for JMX) and the memory usage using the MemoryMXBean. It’s easy to extend to print other statistics using other MXBean types.
Note, that before Java 9, you have to add the tools.jar
of your JDK to the classpath manually. In modular software, you have to add a dependency to the jdk.attach
module.
import static java.lang.management.ManagementFactory.MEMORY_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.io.*;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.*;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import com.sun.tools.attach.*;
public class CmdLineTool
{
static final String CONNECTOR_ADDRESS =
"com.sun.management.jmxremote.localConnectorAddress";
public static void main(String[] args)
{
if(args.length!=1)
System.err.println("Usage: java CmdLineTool <pid>");
else if(printStats(args[0])) return;
System.out.println("Currently running");
for(VirtualMachineDescriptor vmd:VirtualMachine.list())
System.out.println(vmd.id()+"\t"+vmd.displayName());
}
private static boolean printStats(String id)
{
try
{
VirtualMachine vm=VirtualMachine.attach(id);
System.out.println("Connected to "+vm.id());
System.out.println("System Properties:");
for(Map.Entry<?,?> en:vm.getSystemProperties().entrySet())
System.out.println("\t"+en.getKey()+" = "+en.getValue());
System.out.println();
try
{
MBeanServerConnection sc=connect(vm);
MemoryMXBean memoryMXBean =
newPlatformMXBeanProxy(sc, MEMORY_MXBEAN_NAME, MemoryMXBean.class);
getRamInfoHtml(memoryMXBean);
} catch(IOException ex)
{
System.out.println("JMX: "+ex);
}
vm.detach();
return true;
} catch(AttachNotSupportedException | IOException ex)
{
ex.printStackTrace();
}
return false;
}
// requires Java 8, alternative below the code
static MBeanServerConnection connect(VirtualMachine vm) throws IOException
{
String connectorAddress = vm.startLocalManagementAgent();
JMXConnector c=JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
return c.getMBeanServerConnection();
}
static void getRamInfoHtml(MemoryMXBean memoryMXBean)
{
System.out.print("Heap:\t");
MemoryUsage mu=memoryMXBean.getHeapMemoryUsage();
System.out.println(
"allocated "+mu.getCommitted()+", used "+mu.getUsed()+", max "+mu.getMax());
System.out.print("Non-Heap:\t");
mu=memoryMXBean.getNonHeapMemoryUsage();
System.out.println(
"allocated "+mu.getCommitted()+", used "+mu.getUsed()+", max "+mu.getMax());
System.out.println(
"Pending Finalizations: "+memoryMXBean.getObjectPendingFinalizationCount());
}
}
The connect
method of above solution requires Java 8. The alternative for older Java versions looks like
static MBeanServerConnection connect(VirtualMachine vm) throws IOException
{
String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
if(connectorAddress == null)
{
System.out.println("loading agent");
Properties props = vm.getSystemProperties();
String home = props.getProperty("java.home");
String agent = home+File.separator+"lib"+File.separator+"management-agent.jar";
try {
vm.loadAgent(agent);
} catch (AgentLoadException|AgentInitializationException ex) {
throw new IOException(ex);
}
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
while(connectorAddress==null) try {
Thread.sleep(1000);
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
} catch(InterruptedException ex){}
}
JMXConnector c=JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
return c.getMBeanServerConnection();
}