这* *真的是从Java代码启动第二个JVM的最佳方式吗?

时间:2009-08-04 20:01:06

标签: java jvm multiprocessing

这是我的own previous question的后续内容,我有点尴尬地问这个......但无论如何:你将如何以独立于系统的方式从独立的Java程序启动第二个JVM?并且不依赖于例如像JAVA_HOME这样的env变量,因为它可能指向与当前正在运行的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 个答案:

答案 0 :(得分:6)

我认为答案是肯定的。这可能与使用系统无关代码在Java中可以做的一样好。但要注意,即使这只是相对系统独立。例如,在某些系统中:

  1. 可能尚未设置JAVA_HOME变量,
  2. 用于启动JVM的命令名称可能不同(例如,如果它不是Sun JVM),或
  3. 命令行选项可能不同(例如,如果它不是Sun JVM)。
  4. 如果我的目标是在启动(第二个)JVM时实现最大的可移植性,我想我会使用包装器脚本来实现。

答案 1 :(得分:5)

我不清楚你总是想要使用完全相同的参数,类路径或其他什么(特别是-X类的东西 - 例如,为什么孩子需要与其父项相同的堆设置)二次加工。

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

要查看可能的配置设置的范围,您可以查看Eclipse中的“运行配置”设置。那里有相当多的标签配置。

答案 2 :(得分:5)

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

是:

org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable( “Java” 的)

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

它在ant.jar中。 ant是在Apache许可下发布的,所以希望你可以毫不费力地使用它。

答案 3 :(得分:0)

这是一种确定Java可执行文件的方法,该Java可执行文件使用ProcessHandle.current().info().command()运行当前的JVM。

ProcessHandle API也应应该允许获取参数。如果可用,此代码将它们用于新的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(", ")));
        }
    }
}

在Java 9中添加ProcessHandle之前,我做了类似的事情来查询当前JVM的命令行:

  • 让用户传递或配置“命令行PID”命令模板;在Windows下,可能是wmic process where 'processid=%s' get commandline /format:list
  • 使用java.lang.management.ManagementFactory.getRuntimeMXBean().getPid()确定PID。
  • 扩展命令模板;执行;解析其输出。