14

How to get in Java 6+ the list of running JVMs on the localhost and their specs (i.e. Java version, running threads, etc.)?

Does the Java API provide such features? Is there a Java library that can do this?

fsarradin
  • 193
  • 1
  • 1
  • 10

8 Answers8

19

You can use jps, a command-line tool distributed with the jvm. I'm not aware of any normal Java API for it, though. However, JConsole can do what you ask, so I had a look at its source. It was quite scary, but while looking around I found references to the jVisualVM classes, which are OK seeing as you specify Java 6+. Here's what I found:

The classes are all in sun.tools, so firstly you have to find the jconsole.jar that came with your JVM (assumes Sun JVM, of course) and add it to your classpath. Then a quick-n-dirty listing of the active VMs can be got like:

public static void main(final String[] args) {
    final Map<Integer, LocalVirtualMachine> virtualMachines = LocalVirtualMachine.getAllVirtualMachines();
    for (final Entry<Integer, LocalVirtualMachine> entry : virtualMachines.entrySet()) {
        System.out.println(entry.getKey() + " : " + entry.getValue().displayName());
    }
}

Once you've got a reference to your JVMs, you can communicate with them using the Attach API.

Matthew Gilliard
  • 9,298
  • 3
  • 33
  • 48
  • Very interesting! I've also just found this solution http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html#gdhkz . It uses tools.jar in JAVA_HOME/lib (you've to add it in your classpath). – fsarradin Mar 05 '11 at 15:22
  • 1
    When you’re going to communicate with the JVM using the Attach API, why not using the Attach API for [getting the list of JVMs](https://docs.oracle.com/javase/6/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html#list()) in the first place, without that obsolete dependency to `jconsole.jar`? – Holger Jun 08 '18 at 10:23
  • cannot find import sun.tools.jconsole.LocalVirtualMachine – Avinash Apr 23 '21 at 12:43
9

jps in the \jdk\bin directory prints a list of running Java processes but not their specs. Some running conditions are available:

C:\Java\jdk1.6.0_23\bin>jps -mlv
4660  -Dosgi.requiredJavaVersion=1.5 -Xms40m -Xmx512m -XX:MaxPermSize=256m
6996 sun.tools.jps.Jps -mlv -Dapplication.home=C:\Java\jdk1.6.0_23 -Xms8m
Jonathon Faust
  • 12,396
  • 4
  • 50
  • 63
7

You can actually get a list of JVMs using the Attach API, without resorting to using jconsole.jar.

The following demonstrates getting the list of JVMs running locally, getting some properties, connecting to each via JMX, and getting some further information via that connection.

// ...

import com.sun.tools.attach.*;
import javax.management.*;

// ...

    List<VirtualMachineDescriptor> descriptors = VirtualMachine.list();
    for (VirtualMachineDescriptor descriptor : descriptors) {
        System.out.println("Found JVM: " + descriptor.displayName());
        try {
            VirtualMachine vm = VirtualMachine.attach(descriptor);
            String version = vm.getSystemProperties().getProperty("java.runtime.version");
            System.out.println("   Runtime Version: " + version);

            String connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            if (connectorAddress == null) {
                vm.startLocalManagementAgent();
                connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            }

            JMXServiceURL url = new JMXServiceURL(connectorAddress);
            JMXConnector connector = JMXConnectorFactory.connect(url);
            MBeanServerConnection mbs = connector.getMBeanServerConnection();

            ObjectName threadName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
            Integer threadCount = (Integer)mbs.getAttribute(threadName, "ThreadCount");
            System.out.println("    Thread count: " + threadCount);
        }
        catch (Exception e) {
            // ...
        }
    }

A list of the available MXBeans is here. More on JMX in general can be found here, which I got from a comment on another answer to this question. Some code above came from both links.

Note: For me at least, I had to add tools.jar to the bootstrap classpath for this to run:

$ java -Xbootclasspath/a:${JAVA_HOME}/lib/tools.jar -jar <jar>
Community
  • 1
  • 1
shawkinaw
  • 3,190
  • 2
  • 27
  • 30
2

I don't know about Java 6, but I think you can do by running jconsole from the command line.

Also, if you are on a *nix platform you can issue a command like this this:

ps aux | grep java

Good luck!

PacificNW_Lover
  • 4,746
  • 31
  • 90
  • 144
2

This, I found out today, is basically what JPS does: simply list the folder /tmp/hsperfdata_foo/ where foo is the user running the JVM.

For some unknown reason, sometimes the files there won't be deleted when a JVM is killed.

Here are the links to the source code:

PerfDataFile.java

LocalVmManager

Eldelshell
  • 6,683
  • 7
  • 44
  • 63
1

If you need just list of running JVMs with PIDs this works for me:

package my.code.z025.util;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import sun.jvmstat.monitor.HostIdentifier;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;

/**
 * Get list of all local active JVMs
 */
public class ActiveJavaVirtualMachineExplorer {

    /**
     * Represents successfully captured active java virtual machine 
     */
    public static class ActiveVm {
        private int pid;
        private String name;
            ... shortened ...
    }

    /**
     * Represents unsuccessfully captured active java virtual machine, a failure.
     * Keep cause exception. 
     */
    public static class FailedActiveVm extends ActiveVm {
        private Exception cause;
        ... shortened ...
    }

    /**
     * Get list of all local active JVMs.
     * <p> 
     * Returns something like:
     * ActiveVm [pid=7992, name=my.code.z025.util.launch.RunHttpServer]
     * ActiveVm [pid=6972, name=] 
     * ActiveVm [pid=8188, name=my.code.z025.util.launch.RunCodeServer]
     * ActiveVm [pid=4532, name=org.eclipse.jdt.internal.junit.runner.RemoteTestRunner]
     * The pid=6972 must be Eclipse. So this approach is not water tight.
     */
    public static List<ActiveVm> getActiveLocalVms() {
        List<ActiveVm> result = new LinkedList<ActiveVm>();
        MonitoredHost monitoredHost;
        Set<Integer> activeVmPids;
        try {
            monitoredHost = MonitoredHost.getMonitoredHost(new HostIdentifier((String) null));
            activeVmPids = monitoredHost.activeVms();
            for (Integer vmPid : activeVmPids) {
                try {
                    MonitoredVm mvm = monitoredHost.getMonitoredVm(new VmIdentifier(vmPid.toString()));
                    result.add(new ActiveVm(vmPid.intValue(), MonitoredVmUtil.mainClass(mvm, true)));
                    mvm.detach();
                } catch (Exception e) {
                    result.add(new FailedActiveVm(vmPid.intValue(), e));
                }
            }
            return result;
        } catch (java.net.URISyntaxException e) {
            throw new InternalError(e.getMessage());
        } catch (MonitorException e) {
            throw new InternalError(e.getMessage());
        }
    }
}

Application does not have to be run with any special command line parameters to be detectable like this. No need to activate JMX.

Not all JVM apps play well though. For Eclipse I see only PID, but not class name or command line.

You have to add tool.jar to your classpath, it contains packages sun.jvmstat.*. The tool.jar is part of JDK. With some variant of JDK/OS it is on classpath by default, with some you have to add it. Doing so with Maven dependencies is a bit tricky, systemPath is required.

If you want more then just list, check the link from Paŭlo Ebermann - Monitoring and Management Using the JMX API.

VirtualMachine vm = VirtualMachine.attach(pid);

Where the PID you get from the list. Then insert your Agent to (unsuspected) Java (or any JVM) application.

vm.loadAgent(agentJarLibraryFullPath);

Then communicate with your agent via JMXConnector. Your agent can gather all the statistics for you. I have not personally tried that. You can even instrument (modify) run time code this way. Profilers use that.

Espinosa
  • 2,491
  • 24
  • 28
1

Additionally to jps there is the jstack tool, which can print out the stack trace of any java process. The start of its output contains the VM version, then comes a list of threads with their stack traces.

I think all three of jps, jstack and jconsole are implemented based on the Java Management Beans API in javax.management.* and java.lang.management, so look there for more information on how to do this in your own program.


Edit: Here is the documentation index for the management stuff. Especially interesting seems the point Monitoring and Management Using the JMX API.

(And yes, it works not only for the own VM, also for other ones on the same system, and even for ones on remote systems if they expose the right interface.)

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • I believe that `java.lang.management` contains classes while allow you to inspect the JVM that you are currently running in, not others which are on the same computer as you. – Matthew Gilliard Mar 04 '11 at 23:31
0

As far as I know the jps provides the list of process launched using the jvm it belongs to. If you have multiple JDKs installed I don't think it would be of help. If you want a list of all java processes you have to use "ps -ef | grep java". I have seen many times JVsualVM couldn't identify all the java processes running on a specific host. If you requirement is specific to a JVM launched processes the above suggestions would apply.

Rajendra
  • 1,703
  • 2
  • 16
  • 22