【问题标题】:Why JavaCompiler is slow while instantiating a Java class?为什么 JavaCompiler 在实例化 Java 类时很慢?
【发布时间】:2017-02-08 17:30:34
【问题描述】:

我正在使用 JavaCompiler 动态创建 Java 类,对其进行编译并加载到我的应用程序中。

我的问题如下:JavaCompiler 的执行时间比实例化同一类的标准方法慢得多。

这里是一个例子:

static void function() {
        long startTime = System.currentTimeMillis();
        String source = "package myPackage; import java.util.BitSet; public class MyClass{ static {";

        while (!OWLMapping.axiomStack.isEmpty()) {
            source += OWLMapping.axiomStack.pop() + ";";
        }

        source += "} }";

        File root = new File("/java");
        File sourceFile = new File(root, "myPackage/MyClass.java");
        sourceFile.getParentFile().mkdirs();
        Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));

        // Compile source file.
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        compiler.run(null, null, null, sourceFile.getPath());

        // Load and instantiate compiled class.
        URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
        Class<?> cls = Class.forName("myPackage.MyClass", true, classLoader);

        long stopTime = System.currentTimeMillis();
        long elapsedTime = stopTime - startTime;
        System.out.println("EXECUTION TIME: " + elapsedTime);
    }

在测量了这段代码之后,我创建了一个新的 java Class 与 var source 的内容相同来测试性能:它比 JavaCompiler 方式快得多。 (我不能使用标准类,因为在我的应用程序中我需要动态创建它)。 那么,有没有可能提高这段代码的性能呢?还是这种低性能是正常的?

编辑:我还测试了生成的代码是一个简单的 OWLAPI 公理序列:

package myPackage;

public class myClass{

static {

myPackage.OWLMapping.manager.addAxiom(myPackage.OWLMapping.ontology,   myPackage.OWLMapping.factory.getOWLSubClassOfAxiom(/*whatever*/);
myPackage.OWLMapping.manager.addAxiom(myPackage.OWLMapping.ontology,myPackage.OWLMapping.factory.getOWLSubClassOfAxiom(/*whatever*/);
myPackage.OWLMapping.manager.addAxiom(myPackage.OWLMapping.ontology,myPackage.OWLMapping.factory.getOWLSubClassOfAxiom(/*whatever*/);


}

}

这正是变量源包含的内容。 公理的数量取决于用户的输入。

【问题讨论】:

  • StringBuilder 用于source 用于while 循环。编译器不会在循环中优化串联。
  • @4castle 我怀疑这会有很大的不同。
  • 我没有看到你在这里实例化类。请记住,当您的代码必须编写源代码时,将其编译为字节码并然后加载它,而通常您只执行最后一步。
  • 另外,请出示您的其他测试代码。
  • 你在问为什么编译一个类并加载它比使用预编译类慢吗?

标签: java java-compiler-api javacompiler


【解决方案1】:

您有两个可能比较慢的领域(但您的基准测试结合了这两个领域)。

首先是构建包含您的源代码的 Java String。当在不同的语句中附加字符串时,JVM 无法将它们优化为 StringBuilders,这意味着它首先在附加的一侧创建字符串,然后在另一侧创建 String,然后创建第三个 @987654324 @ 产生于两个被附加。这给堆和垃圾回收带来了很大压力,生成了大量几乎立即被垃圾回收的对象。

要解决第一个问题,请创建一个 StringBuilder 并将其命名为 .append(...)

第二个问题是您正在实例化JavaCompiler。用于编译 Java 程序的编译器可能有一个类在顶层驱动它,但它会获取大量支持类来填充其私有字段和嵌入式包含。最后,当运行它时,将创建更多对象来保存代码、Lexer、Parser、CompilationUnit 的 AST,最后是字节码发射器。这意味着那一行代码

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());

可能(同样它们不是独立的基准测试)需要一些时间。

最后,类加载器行与类加载系统交互,并且可能不适用于性能。虽然它对性能的影响很小,但我也会独立地对这条线进行基准测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-10-02
    相关资源
    最近更新 更多