本地主机上正在运行的JVM列表

14

如何在Java 6+中获取本地主机上正在运行的JVM列表及其规格(即Java版本、运行线程等)?

Java API是否提供此类功能?是否有Java库可以实现这一点?


1
VisualVM可以做到,所以一定有方法,虽然可能不是很容易。 - biziclop
8个回答

19
您可以使用 Java 虚拟机附带的命令行工具 jps。我不知道是否有任何正常的 Java API 可以实现,但是 JConsole 可以实现您所需的功能,因此我看了一下它的源代码。虽然内容有点吓人,但在查找过程中,我找到了与 jVisualVM 相关的类,只要指定 Java 6+,便可使用这些类。以下是我发现的内容:
这些类都在 sun.tools 中,因此首先需要找到随 JVM 一起提供的 jconsole.jar 并将其添加到类路径中。然后可以通过以下快速简单的方法来获取活动 VM 的列表:
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());
    }
}

一旦你获得了对JVM的引用, 你可以使用Attach API与它们进行通信。


非常有趣!我也刚刚发现了这个解决方案http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html#gdhkz。它使用JAVA_HOME/lib中的tools.jar(您必须将其添加到类路径中)。 - fsarradin
1
当您要使用Attach API与JVM通信时,为什么不首先使用Attach API来获取JVM列表,而不需要那个过时的依赖项jconsole.jar呢? - Holger
无法找到导入sun.tools.jconsole.LocalVirtualMachine的内容。 - Avinash

9

jps 命令在 \jdk\bin 目录下列出正在运行的 Java 进程列表,但不包含它们的规格。一些运行条件是可用的:

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

7
您可以使用Attach API获取JVM列表,而无需使用jconsole.jar
以下演示了如何获取正在本地运行的JVM列表,获取一些属性,通过JMX连接到每个JVM,并通过该连接获取更多信息。
// ...

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) {
            // ...
        }
    }

一个可用的MXBean列表在这里。更多关于JMX的一般信息可以在这里找到,这是我从另一个答案的评论中得到的。上面的一些代码来自这两个链接。 注意:至少对我来说,我必须将tools.jar添加到引导类路径中才能运行此程序:
$ java -Xbootclasspath/a:${JAVA_HOME}/lib/tools.jar -jar <jar>

LocalVirtualMachine在Java8中已不再可用,这个答案现在是正确的。 - loicmathieu

2

我不知道Java 6,但我认为你可以通过从命令行运行jconsole来完成。

此外,如果您在*nix平台上,可以发出如下命令:

ps aux | grep java

祝你好运!


2
今天我发现,JPS基本上只是列出文件夹/tmp/hsperfdata_foo/,其中foo是运行JVM的用户。由于某些未知原因,有时当JVM被杀死时,这里的文件不会被删除。以下是源代码链接:PerfDataFile.javaLocalVmManager

1
如果您只需要列出正在运行的JVM及其进程ID,可以使用以下方法:
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());
        }
    }
}

应用程序不必使用任何特殊的命令行参数来进行检测。无需激活JMX。
但并不是所有的JVM应用程序都能很好地运行。对于Eclipse,我只看到PID,而不是类名或命令行。
您需要将tool.jar添加到类路径中,其中包含sun.jvmstat.*包。 tool.jar是JDK的一部分。对于某些JDK / OS的变体,默认情况下它在类路径上,但对于某些变体则需要手动添加。使用Maven依赖项进行此操作有点棘手,需要使用systemPath。
如果您想获得更多信息,请查看Paŭlo Ebermann的链接-使用JMX API进行监视和管理
VirtualMachine vm = VirtualMachine.attach(pid);

从列表中获取PID,然后将您的代理插入到(不可疑的)Java(或任何JVM)应用程序中。
vm.loadAgent(agentJarLibraryFullPath);

然后通过JMXConnector与您的代理通信。您的代理可以为您收集所有统计信息。我个人没有尝试过这样做。您甚至可以通过这种方式仪器化(修改)运行时代码。分析器使用它。


1

除了jps之外,还有jstack工具,可以打印出任何Java进程的堆栈跟踪。其输出的开头包含VM版本,然后是一系列带有其堆栈跟踪的线程列表。

我认为jps、jstack和jconsole三者都是基于Java Management Beans API在javax.management.*java.lang.management中实现的,因此在这里查找更多关于如何在您自己的程序中执行此操作的信息。


编辑:这里是管理相关的文档索引

特别有趣的是使用JMX API进行监控和管理

(是的,它不仅适用于自己的VM,也适用于同一系统上的其他VM,甚至适用于远程系统上的VM,只要它们公开了正确的接口。)


我相信 java.lang.management 包含了一些类,可以让你检查当前正在运行的 JVM,而不是同一台计算机上的其他 JVM。 - Matthew Gilliard

0
据我所知,jps提供了使用它所属的jvm启动的进程列表。如果您安装了多个JDK,则我认为它不会有帮助。 如果您想要所有java进程的列表,您必须使用“ps -ef | grep java”。我已经看到很多次JVsualVM无法识别特定主机上运行的所有java进程。如果您的要求特定于启动的JVM进程,则上述建议适用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接