2

I'm using JavaCompiler to dyamically create a Java class, compile it and load in my application.

My problem is the following: the execution time with JavaCompiler is much slower than the standard way to instantiate the same class.

Here an example:

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);
    }

After measuring this code, I created a new java Class with the same content of the var source to test the performance: it is much faster than the JavaCompiler way. (I cannot use a standard class because in my application I need to create it dynamically). So, is it possible to improve the performance of this code? Or this low performance is normal?

EDIT: the generated code I also tested is a simple sequence of OWLAPI axioms:

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*/);


}

}

and this exactly what the variable source contains. The number of axioms depends on the user's input.

1 Answers1

1

You have two areas which are likely to be slow (but your benchmarks combine the two areas).

The first is in building the Java String which contains your source code. When appending Strings across different statements, the JVM can't optimize them into StringBuilders which means that first it creates the string on one side of the append, then the String on the other, then it creates a third String resulting from the two being appended. This puts a lot of pressure on the heap and garbage collection, generating lots of objects which are nearly immediately garbage collected.

To fix the first problem, create a StringBuilder and call it's .append(...).

The second problem is that you are instantiating a JavaCompiler. The compiler used to compile Java programs may have one class driving it at the top level, but it will source in tons of supporting classes to fill out its private fields and the embedded includes. Finally, when running that, more objects will be created to hold the code, the Lexer, the Parser, the AST of the CompilationUnit, and eventually the byte-code emitter. This means that the one lines of code

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

Are likely (again they are not independently benchmarked) to take some time.

Finally, the class loader lines interact with the class loading system, and might be poorly adapted for performance. While it's a smaller chance it's a big performance hit, I'd benchmark that line independently too.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138