4

If you set the "com.sun.management.jmxremote" system property when you start a JVM, you can run jconsole or visualvm and connect to that local mBeanServer. I want to do the same thing they are doing but can't figure out how.

Is there a service URL you can use to identify a locally running JVM?

I know I could do this by setting up a jmxmp or rmi listener on a specific port and then connecting to that port, but I don't want to do that because it means I have to manage the ports and know which port goes to which jvm (we run multiple jvms on the same server).

2 Answers2

7

Thanks to Nicholas and Scott for the post and discussion. Was able to piece this together based on that info and some trial and error.

Sharing so others don't have to do the same leg work. This will attach to a JVM and get its memory usage.

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import com.sun.tools.attach.spi.AttachProvider;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class AttachFun {

    public static void main(String[] args) throws Exception {
        final AttachProvider attachProvider = AttachProvider.providers().get(0);

        VirtualMachineDescriptor descriptor = null;
        for (VirtualMachineDescriptor virtualMachineDescriptor : attachProvider.listVirtualMachines()) {
            if (pickThisOne(virtualMachineDescriptor)) {
                descriptor = virtualMachineDescriptor;
                break;
            }
        }

        if (descriptor == null) throw new RuntimeException("You didn't pick one");

        final VirtualMachine virtualMachine = attachProvider.attachVirtualMachine(descriptor);
        virtualMachine.loadAgent("/System/Library/Frameworks/JavaVM.framework/Home/lib/management-agent.jar", "com.sun.management.jmxremote");
        final Object portObject = virtualMachine.getAgentProperties().get("com.sun.management.jmxremote.localConnectorAddress");

        final JMXServiceURL target = new JMXServiceURL(portObject + "");
        final JMXConnector connector = JMXConnectorFactory.connect(target);
        final MBeanServerConnection remote = connector.getMBeanServerConnection();

        final ObjectName memory = new ObjectName("java.lang:type=Memory");
        CompositeData cd = (CompositeData) remote.getAttribute(memory, "HeapMemoryUsage");

        final Long used = (Long) cd.get("used");
        System.out.println(used);

    }

    private static boolean pickThisOne(VirtualMachineDescriptor virtualMachineDescriptor) {
        // TODO
        return false;
    }
}
David Blevins
  • 19,178
  • 3
  • 53
  • 68
  • 3
    Nice sample, and you could also found similar detailed example from here: http://blog.fastconnect.fr/?p=385 –  Dec 04 '12 at 21:43
  • 1
    With JDK 8, it's now much simpler to connect to load the agent and get the local address. You should use the `virtualMachine.startLocalManagementAgent` method. – Alexis Hassler Jun 29 '15 at 07:48
1

You need the Attach API. It will locate running [Attach API capable] JVMs on the same host and return meta-data on each as well as enable a JMXConnector. It's basically what you see when you start JConsole and the connect menu displays running JVMs, including Java 1.6+ instances that were not started with any special JMX command line directives.

//Nicholas

Nicholas
  • 15,916
  • 4
  • 42
  • 66
  • The attach API gets me a handle to the JVM, but not to the mBeanServer in that JVM. The javadoc for the attach API shows an example of using it to load an agent remotely with this call: – Scott Greenman Dec 31 '10 at 15:20
  • Right. If you load the JRE's build in management agent (/jre/lib/management-agent.jar) and specify the listening port (eg. vm.loadAgent(agent, "com.sun.management.jmxremote.port=5000");), then you can use the JMX Remoting API to connect to the MBeanServer on port 5000. If you were looking for a way to access the MBeanServer in a JVM without port access...... I am not sure.... you might be able to develop a FIFO based protocol or something. – Nicholas Dec 31 '10 at 16:30
  • I wasn't looking to connect without port access... I just didn't want to have to specify which port to use. Turns out if you don't specify a port (i.e. call vm.loadAgent(agent, "com.sun.management.jmxremote")) then the jvm picks a random high port. I can then call vm.getAgentProperties().get("com.sun.management.jmxremote.localConnectorAddress") to get a URL to connect to the mBeanServer. Thanks Nicholas for all of your help! Scott Greenman familysearch.org – Scott Greenman Dec 31 '10 at 23:04