2

我正在使用 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);
    }

在测量了这段代码之后,我创建了一个与 var内容相同的新 java Class来测试性能:它比 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*/);


}

}

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

4

1 回答 1

1

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

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

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

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

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

可能(同样它们没有独立进行基准测试)需要一些时间。

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

于 2017-02-08T18:21:24.717 回答