【问题标题】:Java runtime compilation and class file generationJava 运行时编译和类文件生成
【发布时间】:2015-11-06 06:03:19
【问题描述】:

处理需要编译多个源文件(.java) 并在运行时生成相应的类文件(.class) 的应用程序,这发生在应用程序启动时。 p>

目前使用以下代码生成类:

int errorCode = com.sun.tools.javac.Main.compile(new String[] {
        "-cp", classPath,
        "-d", pOutputPath,
        srcFile.getAbsolutePath() },new PrintWriter(out));

每个 *.java 文件几乎都只有大约 40 行代码和一个方法。但是上面给出的代码编译时间大约是每个文件 2 秒

应用程序有超过 1000 个或有时 2000 个 java 文件。因此应用程序启动时间超过 2000 或 4000 秒,这是不可取的。

com.sun.tools.javac.Main.compile 的任何替代方案?

还是一种更好或更快的运行时编译和类文件生成方式?

我不能使用多线程,因为环境是单线程的。

【问题讨论】:

  • 你不能使用CachedThreadPool 并触发作业来编译文件吗?
  • 实际上一个线程正在遍历目录中的文件并进行编译。如果我也有多个线程,那么我需要有很多线程安全措施,如锁、同步等。寻找更快的 Main.compile 替代方案..
  • 你真的需要每次启动都编译类吗?

标签: java javac .class-file runtime-compilation


【解决方案1】:

无需一次编译一个源文件。您可以一次性编译所有这些(只需将它们全部放入您的String[])。

这样会快得多,因为两秒钟的大部分时间都花在了初始化上,而只有一小部分花在了编译源文件上。

假设 - 作为估计 - 编译源文件花费 0.2 秒,初始化事物花费 1.8 秒。然后编译 2000 个文件需要 1.8 + 2000 * 0.2 = 401.8 秒。这只是一个猜测;当您真正尝试时,它可能会有所不同。

您可能还想检查自上次编译它们以来源文件是否确实发生了变化,并且只有在其中至少一个发生变化时才再次编译。

证明等待的合理性要容易得多 - 鉴于上述猜测 - 当代码发生实际变化时比没有发生变化时启动需要 7 分钟。

【讨论】:

  • 没有编译器需要 0.2 秒来编译单个源文件。我为一种功能比 Java 多得多的语言编写了一个编译器,初始化需要 0.3 秒,每个源文件大约需要 10 毫秒。
  • @Clashsoft 是的,我已经编写了一个 java 编译器。我在这里提出一个如何计算的建议;正如 OP 所说,编译 1 个源文件需要 2 秒,你的示例需要 0.31 秒来编译一个源文件显然不适用。当然,持续时间取决于源文件长度/复杂性、计算机速度等。
【解决方案2】:

为什么不让操作系统为你处理呢?

我不确定这一点,但我认为您可以尝试以下方法:

String path = "srcFolderPath";
File dir = new File(path);
for (File src : dir.listFiles()) {

    try {
        ProcessBuilder pb = new ProcessBuilder()
                .directory(dir.getAbsoluteFile())
                .command("javac " + src.getName()); // or whatever command params

        Process p = pb.start();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

PS:我没有测试过,你可以试试。

【讨论】:

  • 为什么你认为这样更快?
【解决方案3】:

最近我在类似的情况下使用了ToolProvider:javax.tools.ToolProvider.getSystemJavaCompiler()

例子:

protected static boolean compileFiles(List<String> additionalOptions,ArrayList<String> sources){
  JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
  if (compiler == null)   return false;
  StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null,null);
  List<String> options=new ArrayList<String>();
  options.add("-d");
  options.add(getOutBuildDir().getAbsolutePath());
  options.add("-s");
  options.add(getOutSrcDir().getAbsolutePath());
  options.add("-verbose");
  if (additionalOptions != null)   options.addAll(additionalOptions);
  Iterable<? extends JavaFileObject> compilationUnits=fileManager.getJavaFileObjectsFromStrings(sources);
  CompilationTask task=compiler.getTask(mOutputWriter,fileManager,null,options,null,compilationUnits);
  return task.call();
}

从这里:http://www.programcreek.com/java-api-examples/javax.tools.ToolProvider

在运行时编译的另一种方法是在内存编译中使用例如JIMCy 库(https://github.com/Krever/JIMCy)。 它实际上是用 Scala 编写的,但有 Java API。

【讨论】:

  • 这当然是调用 Java 编译器的更好方法(但除非您还执行其他操作,否则不会使其更快)。然而,目前它是一个仅限链接的答案,并且链接在一段时间后就会过时。请包括相关信息并写在您自己的答案中。
猜你喜欢
  • 2018-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 2010-12-11
  • 1970-01-01
  • 2015-05-27
  • 2014-08-06
相关资源
最近更新 更多