这是我的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 ...,...)。
答案 0 :(得分:6)
我认为答案是肯定的。这可能与使用系统无关代码在Java中可以做的一样好。但要注意,即使这只是相对系统独立。例如,在某些系统中:
如果我的目标是在启动(第二个)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的命令行:
wmic process where 'processid=%s' get commandline /format:list
。java.lang.management.ManagementFactory.getRuntimeMXBean().getPid()
确定PID。