这真的是从Java代码启动第二个JVM的最佳方式吗?
这是我的后续行动,我有点不好意思问这个。。。但无论如何:您如何以独立于系统的方式从独立Java程序启动第二个JVM?并且不依赖例如JAVA_HOME之类的env变量,因为它可能指向与当前运行的JRE不同的JRE。我想出了下面的代码,它实际上可以工作,但感觉有点尴尬:这真的是从Java代码启动第二个JVM的最佳方式吗?,java,jvm,multiprocessing,Java,Jvm,Multiprocessing,这是我的后续行动,我有点不好意思问这个。。。但无论如何:您如何以独立于系统的方式从独立Java程序启动第二个JVM?并且不依赖例如JAVA_HOME之类的env变量,因为它可能指向与当前运行的JRE不同的JRE。我想出了下面的代码,它实际上可以工作,但感觉有点尴尬: public static void startSecondJVM() throws Exception { String separator = System.getProperty("file.separator");
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…,…)启动的。我不清楚您是否总是希望使用完全相同的参数、类路径或其他任何参数(特别是-X类的东西——例如,为什么子进程需要与父进程相同的堆设置)启动辅助进程时 我更喜欢使用某种外部配置来为孩子们定义这些属性。这需要更多的工作,但我认为最终你需要灵活性
要查看可能的配置设置的范围,您可以查看Eclipse中的“运行配置”设置。其中有很多值得配置的选项卡。我认为答案是“是”。这可能与使用系统独立代码在Java中所做的一样好。但请注意,即使这样,也只是相对独立于系统。例如,在某些系统中:
如果我的目标是在启动(第二个)JVM时实现最大的可移植性,我想我会使用包装器脚本来实现这一点。查找代码当前运行的java可执行文件(即问题示例代码中的“path”变量)ApacheAnt中有一个实用方法可以帮助您,您不必使用ant构建代码,只需将其用作库,就可以使用这个方法 它是: org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable(“java”) 它负责处理其他人提到的不同JVM供应商的特殊情况(看看它的源代码,有比我想象的更多的特殊情况)
它位于ant.jar中。ant是在Apache许可证下分发的,因此希望您可以随意使用它。以下是一种使用
ProcessHandle.current().info().command()确定运行当前JVM的java可执行文件的方法。
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.lang.ProcessBuilder.Redirect;
导入java.lang.management.ManagementFactory;
导入java.util.LinkedList;
导入java.util.List;
导入java.util.Optional;
导入java.util.stream.collector;
导入java.util.stream.stream;
公共类TestStartJvm{
公共静态void main(字符串[]args)引发异常{
ProcessHandle.Info currentProcessInfo=ProcessHandle.current().Info();
List newProcessCommandLine=newlinkedlist();
newProcessCommandLine.add(currentProcessInfo.command().get());
可选的currentProcessArgs=currentProcessInfo.arguments();
if(currentProcessArgs.isPresent()){//我知道orElse,但有时isPresent+get很方便
for(字符串arg:currentProcessArgs.get()){
newProcessCommandLine.add(TestStartJvm.class.getName().equals(arg)?TargetMain.class.getName():arg);
}
}否则{
System.err.println(“不知道所有进程参数,返回到传递的参数数组”);
添加(“-classpath”);
添加(ManagementFactory.getRuntimeMXBean().getClassPath());
newProcessCommandLine.add(TargetMain.class.getName());
newProcessCommandLine.addAll(参数列表);
}
ProcessBuilder newProcessBuilder=newProcessBuilder(newProcessCommandLine).redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process newProcess=newProcessBuilder.start();
System.out.format(“%s:进程%s已启动%n”,TestStartJvm.class.getName(),newProcessBuilder.command());
System.out.format(“进程退出,状态为%s%n”,newProcess.waitFor());
}
静态类TargetMain{
公共静态void main(字符串[]args){
System.out.format(“in%s:PID%s,args:%s%n”,TargetMain.class.getName(),ProcessHandle.current().PID(),
Stream.of(args.collect(collector.joining(“,”));
}
}
}
在Java 9中添加ProcessHandle
之前,我做了如下操作来查询当前JVM的命令行:
- 让用户传递或配置“PID到命令行”命令模板;在Windows下,这可能是
wmic进程,其中'processid=%s'获取命令行/格式:列表
- 使用
确定PIDjava.lang.management.ManagementFactory.getRuntimeMXBean().getPid()
- 展开命令模板;执行;解析其输出