在运行的JVM中,如何以编程方式确定启动时使用的JVM选项?

10

背景:我正在对一个通过多层间接调用启动的Java应用程序进行性能测试,因此我不确定应用程序是否使用了我认为的标志启动。我希望在应用程序开始性能测试之前包含一个健全性检查,并在测试后的结果中包括有关JVM如何调整的信息,例如:

  • 使用了哪种垃圾收集器?
  • 是否正在活跃地进行CPU分析?
  • 是否记录了gc活动?
  • 处于-Xint-Xmixed模式下?
  • 设置了-XX:ParallelGCThreads吗?如果是,是什么值?如果没有,则构建默认值是什么?
  • -XX:UseCompressedOops是开启还是关闭?
  • 等等。

在Java代码中有没有一种方式(在运行时)查询其所在JVM使用的实际选项? (假设我看不到启动我的命令行,因此无法重新解析这些标志。)

如果没有一般性的方法来确定这一点,那么特定于特定JVM实现的答案也是可以的。

更新:

重要的是解决方案能够知道任何未在命令行上显式提供的值的默认值。否则,查找给定组合JVM/平台/版本/架构的每个参数的默认值将涉及大量(容易出错的)工作。我正在测试各种JVM,因此不想手动确定每个jvm发布版中每个参数的默认设置。


你可以使用 ps -ef 列出由 PID 启动的 JVM 进程,并在那里查看该进程的所有输入参数。这对于任何类型的 JVM 都适用。 - Aleš
@AlesJ。OP 表示命令行方式不可行。 - Oleg Mikheev
@AlesJ. -- 这只会给我明确设置的值,所以我不会得到任何关于JVM隐式默认值的信息(请参见问题的更新编辑)。 :-( - Mickalot
1
我明白了,太糟糕了,本来这个方法应该行的。我觉得没有一个适用于所有虚拟机的解决方案,每个虚拟机都需要采用特定的方法。一般情况下,你可以在文档中很容易找到未指定参数的默认值。 - Aleš
3个回答

7

您可以通过以下方式获取命令行参数:

ManagementFactory.getRuntimeMXBean().getInputArguments();

这也是我第一个想法,但它能用来确定具体的事情吗,比如使用了哪个垃圾收集器是否正在进行CPU性能分析等等?我认为你应该在你的回答中详细说明。 - FThompson
我非常感激这个(这是我尝试的第一件事),但我不想要它的原因是,如果JVM选项未指定,则它将设置为JVM的默认值,并且它不在此列表中(该列表仅包含明确指定的参数)。 因此,它无法回答“此构建的默认值是什么?” 我正在许多不同的机器上进行测试,每台机器可能具有略有不同的构建和略有不同的默认值。 - Mickalot

5
以下是Java 7代码,将列出由-XX:+PrintFlagsFinal返回的所有JVM选项。它尝试使用反射访问受包保护的Flag辅助类(自Java 6以来可用),如果失败,则退回到HotSpotDiagnosticMXBean.getDiagnosticOptions()
// load the diagnostic bean first to avoid UnsatisfiedLinkError
final HotSpotDiagnosticMXBean hsdiag = ManagementFactory
        .getPlatformMXBean(HotSpotDiagnosticMXBean.class);
List<VMOption> options;
try {
    final Class<?> flagClass = Class.forName("sun.management.Flag");
    final Method getAllFlagsMethod = flagClass.getDeclaredMethod("getAllFlags");
    final Method getVMOptionMethod = flagClass.getDeclaredMethod("getVMOption");
    getAllFlagsMethod.setAccessible(true);
    getVMOptionMethod.setAccessible(true);
    final Object result = getAllFlagsMethod.invoke(null);
    final List<?> flags = (List<?>) result;
    options = new ArrayList<VMOption>(flags.size());
    for (final Object flag : flags) {
        options.add((VMOption) getVMOptionMethod.invoke(flag));
    }
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
        | InvocationTargetException | ClassCastException e) {
    if (hsdiag != null) {
        // only includes writable external flags
        options = hsdiag.getDiagnosticOptions();
    } else {
        options = Collections.emptyList();
    }
}
final Map<String, VMOption> optionMap = new TreeMap<>();
for (final VMOption option : options) {
    optionMap.put(option.getName(), option);
}
for (final VMOption option : optionMap.values()) {
    System.out.println(option.getName() + " = " + option.getValue() + " (" +
            option.getOrigin() + ", " +
            (option.isWriteable() ? "read-write" : "read-only") + ")");
}
System.out.println(options.size() + " options found");

使用7u71版本,我得到了663个选项,或者使用-XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions,得到了779个选项。


2
您可以使用JMX客户端(如VisualVM),然后调用getVMOption(String name),请参见HotSpotDiagnosticMXBean
或者,如果您至少能传入一组标志以启用JVM日志记录,则应该是--XX:+LogVMOutput -XX:LogFile=jvm.log,然后从您的应用程序中解析日志输出。日志包含启动JVM时使用的所有标志/参数。
另一种选择是使用ps -ef列出由PID启动的JVM进程,然后您可以看到该进程的所有输入参数。这对于任何JVM类型都适用。

HotSpotDiagnosticMXBean 不是 Oracle JVM 特定的吗? - Oleg Mikheev
@OlegMikheev - 虽然不是理想的解决方案,但我会接受任何JVM特定的解决方案(大多数用户可能会使用Hotspot JVM)。 但是HotSpotDiagnosticMXBean看起来并不是正确的bean...它告诉我哪些诊断措施已经就位(例如在执行GC时应记录什么),而不是VM如何调整。 - Mickalot
@AlesJ. -- 我尝试了几个Sun/Oracle J2SE jdks(主要是在Linux上的1.6/1.7 32/64位),但似乎没有一个能够识别-XX:+LogVMOutput。我也没有在这里看到它:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html。你使用的是哪个接受它的JVM? - Mickalot
这都是与Hotspot相关的。尝试使用“-XX:+ UnlockDiagnosticVMOptions”,它应该适用于Hotspot 6和7。 - Aleš
我选择这个作为“采纳”的答案,因为我没有找到一种方式来接受@AlesJ在问题中的评论,即“没有供应商独立的解决方案,对于每个虚拟机,您都需要一个特定的方法。”不幸的是,回答“如何做...”的问题是“你不能做到”。 - Mickalot

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