41

这是我自己之前的问题的后续,我有点不好意思问这个......但无论如何:你将如何以独立于系统的方式从独立的 Java 程序启动第二个 JVM?并且不依赖于例如像 JAVA_HOME 这样的环境变量,因为它可能指向与当前运行的 JRE 不同的 JRE。我想出了以下代码,它确实有效,但感觉有点尴尬:

public static void startSecondJVM() throws Exception {
    String separator = System.getProperty("file.separator");
    String classpath = System.getProperty("java.class.path");
    String path = System.getProperty("java.home")
                + separator + "bin" + separator + "java";
    ProcessBuilder processBuilder = 
                new ProcessBuilder(path, "-cp", 
                classpath, 
                AnotherClassWithMainMethod.class.getName());
    Process process = processBuilder.start();
    process.waitFor();
}

此外,当前运行的 JVM 可能已使用第二个 JVM 不知道的其他一些参数(-D、-X...、...)启动。

4

4 回答 4

6

我认为答案是“是”。这可能与使用系统独立代码在 Java 中所做的一样好。但请注意,即使这也只是相对系统独立的。例如,在某些系统中:

  1. JAVA_HOME 变量可能尚未设置,
  2. 用于启动 JVM 的命令名称可能不同(例如,如果它不是 Sun JVM),或者
  3. 命令行选项可能不同(例如,如果它不是 Sun JVM)。

如果我的目标是在启动(第二个)JVM 时实现最大的可移植性,我想我会使用包装脚本来实现。

于 2009-08-05T00:06:41.100 回答
5

我不清楚在启动辅助进程时您是否总是希望使用完全相同的参数、类路径或其他任何东西(尤其是 -X 类型的东西 - 例如,为什么子进程需要与其父进程相同的堆设置)。

我更喜欢使用某种外部配置来为孩子定义这些属性。这需要更多的工作,但我认为最终你将需要灵活性。

要查看可能的配置设置范围,您可以查看 Eclipse 中的“运行配置”设置。那里有很多值得配置的选项卡。

于 2009-08-04T20:23:05.527 回答
5

要找到您的代码当前正在运行的 java 可执行文件(即问题示例代码中的“路径”变量),apache ant 中有一个实用方法可以帮助您。您不必使用 ant 构建代码 - 只需将其用作库,就可以使用这种方法。

这是:

org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable("java")

它负责处理其他人提到的不同 JVM 供应商的特殊情况。(看看它的源代码,有比我想象的更多的特殊情况。)

它在 ant.jar 中。ant 是根据 Apache 许可证分发的,因此希望您可以轻松使用它。

于 2011-07-27T22:55:02.267 回答
0

这是一种确定运行当前 JVM 的 java 可执行文件的方法ProcessHandle.current().info().command()

ProcessHandleAPI 还应该允许获取参数。如果可用,此代码将它们用于新的 JVM,仅将当前类名替换为另一个示例类。(如果您不知道其名称,则在参数中查找当前主类会变得更加困难,但在此演示中,它只是“this”类。也许您想重用相同的 JVM 选项或其中一些选项,而不是程序论据。)

但是,对我来说(openjdk 版本 11.0.2,Windows 10),ProcessInfo.arguments()是空的,所以回退else路径被执行。

package test;
import java.lang.ProcessBuilder.Redirect;
import java.lang.management.ManagementFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestStartJvm {
    public static void main(String[] args) throws Exception {
        ProcessHandle.Info currentProcessInfo = ProcessHandle.current().info();
        List<String> newProcessCommandLine = new LinkedList<>();
        newProcessCommandLine.add(currentProcessInfo.command().get());

        Optional<String[]> currentProcessArgs = currentProcessInfo.arguments();
        if (currentProcessArgs.isPresent()) { // I know about orElse, but sometimes isPresent + get is handy
            for (String arg: currentProcessArgs.get()) {
                newProcessCommandLine.add(TestStartJvm.class.getName().equals(arg) ? TargetMain.class.getName() : arg);
            }
        } else {
            System.err.println("don't know all process arguments, falling back to passed args array");
            newProcessCommandLine.add("-classpath");
            newProcessCommandLine.add(ManagementFactory.getRuntimeMXBean().getClassPath());
            newProcessCommandLine.add(TargetMain.class.getName());
            newProcessCommandLine.addAll(List.of(args));
        }

        ProcessBuilder newProcessBuilder = new ProcessBuilder(newProcessCommandLine).redirectOutput(Redirect.INHERIT)
                .redirectError(Redirect.INHERIT);
        Process newProcess = newProcessBuilder.start();
        System.out.format("%s: process %s started%n", TestStartJvm.class.getName(), newProcessBuilder.command());
        System.out.format("process exited with status %s%n", newProcess.waitFor());
    }

    static class TargetMain {
        public static void main(String[] args) {
            System.out.format("in %s: PID %s, args: %s%n", TargetMain.class.getName(), ProcessHandle.current().pid(),
                    Stream.of(args).collect(Collectors.joining(", ")));
        }
    }
}

ProcessHandleJava 9 中添加之前,我做了这样的事情来查询当前 JVM 的命令行:

  • 让用户传递或配置一个“PID to command-line”命令模板;在 Windows 下,这可能是wmic process where 'processid=%s' get commandline /format:list.
  • 使用 确定 PID java.lang.management.ManagementFactory.getRuntimeMXBean().getPid()
  • 扩展命令模板;执行; 解析它的输出。
于 2020-12-03T16:19:42.193 回答