2017-02-08 9 views
2

Ich benutze JavaCompiler, um eine Java-Klasse dynamisch zu erstellen, zu kompilieren und in meiner Anwendung zu laden.Warum JavaCompiler ist langsam beim Instanziieren einer Java-Klasse?

Mein Problem ist das folgende: die Ausführungszeit mit JavaCompiler ist viel langsamer als der Standard Weg, um die gleiche Klasse zu instanziieren.

Hier ein Beispiel:

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

diesen Code Nach der Messung ich mit dem gleichen Inhalt der var Quelle eine neue Java-Klasse geschaffen, um die Leistung zu testen: es ist viel schneller als der JavaCompiler Weg . (Ich kann keine Standardklasse verwenden, da ich sie in meiner Anwendung dynamisch erstellen muss). Also, ist es möglich, die Leistung dieses Codes zu verbessern? Oder diese geringe Leistung ist normal?

EDIT: der erzeugte Code, den ich auch eine einfache Abfolge von OWLAPI Axiome getestet ist:

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


} 

} 

und genau das, was die Variable Quelle enthält. Die Anzahl der Axiome hängt von der Eingabe des Benutzers ab.

+1

Verwenden Sie einen 'StringBuilder' für die' Quelle' für die 'while' Schleife. Der Compiler optimiert die Verkettung in einer Schleife nicht. – 4castle

+0

@ 4castle Ich bezweifle, dass das einen großen Unterschied machen würde. – Kayaman

+0

Ich sehe nicht, dass Sie hier die Klasse instanziieren. Denken Sie daran, dass, wenn Ihr Code die Quelle schreiben muss, diese zu Bytecode kompiliert und dann geladen wird, während Sie normalerweise nur den letzten Schritt machen. –

Antwort

1

Sie haben zwei Bereiche, die wahrscheinlich langsam sind (aber Ihre Benchmarks kombinieren die beiden Bereiche).

Die erste ist im Aufbau der Java String, die Ihren Quellcode enthält. Beim Anhängen von Strings über verschiedene Anweisungen kann die JVM sie nicht in StringBuilders optimieren, was bedeutet, dass zuerst die Zeichenfolge auf der einen Seite des Anhangs erstellt wird, dann die String auf der anderen, dann wird eine dritte String erzeugt, die sich aus den beiden ergibt angehängt. Dies setzt die Heap- und Garbage-Collection stark unter Druck und erzeugt viele Objekte, die fast sofort als Müll gesammelt werden.

Um das erste Problem zu beheben, erstellen Sie eine StringBuilder und rufen Sie es .append(...). Das zweite Problem besteht darin, dass Sie einen JavaCompiler instanziieren. Der Compiler, der zum Kompilieren von Java-Programmen verwendet wird, kann eine Klasse haben, die ihn auf der obersten Ebene antreibt, aber er wird in Tonnen von unterstützenden Klassen zur Verfügung stellen, um seine privaten Felder und die eingebetteten Includes auszufüllen. Wenn Sie das ausführen, werden schließlich weitere Objekte erstellt, die den Code, den Lexer, den Parser, den AST der CompilationUnit und schließlich den Bytecode-Emitter enthalten. Dies bedeutet, dass die einen Zeilen des Codes wahrscheinlich (wiederum sind sie nicht unabhängig bewertet) wahrscheinlich eine gewisse Zeit brauchen.

Schließlich interagieren die Klassenladerzeilen mit dem Klassenladesystem und sind möglicherweise schlecht für die Leistung angepasst. Während es eine kleinere Chance ist, ist es ein großer Leistungshit, ich würde diese Zeile auch unabhängig benchmarken.

Verwandte Themen