2009-05-22 19 views
7

Ich versuche, eine Java-.class-Datei dynamisch zu laden und durch Reflektion aufzurufen.Java Classloading läuft extrem langsam?

Ich habe eine Klasse namens Foo; Es hat einen leeren Konstruktor und hat eine Methode namens doit(), die ein String-Argument annimmt und einen String zurückgibt. Außerdem kehrt es die Zeichenfolge um.

Hier ist mein Code:

URL url = new URL("file://C:/jtest/"); 
    URLClassLoader loader = new URLClassLoader(new URL[]{url}); 
    Class<?> cl = loader.loadClass("Foo"); 
    Constructor<?> cons = cl.getConstructor((Class[])null); 
    Object ins = cons.newInstance(new Object[]{}); 
    Method meth = cl.getDeclaredMethod("doit", String.class); 
    Object ret = meth.invoke(ins, new Object[]{"!dlroW olleH"}); 
    System.out.println((String)ret); 

Da diesen Druck erwartet "Hallo Welt!". Es dauert jedoch etwa 30 Sekunden abzuschließen. Ich weiß, Reflexion ist langsam, aber ich erwarte, dass es 10 ms oder so ist.

Ich benutze Eclipse mit JRE 1.6.0_13, und ich verwende Windows Vista.

Was mache ich hier falsch?

Danke.

Bearbeiten: Ich habe den Code profiliert, und alle seine Zeit wird in der dritten Zeile (loadClass()) verwendet. Alles andere passiert sofort.

Edit: Ich habe den Code in eine Schleife gesetzt; Die langsame Funktion wird irgendwie optimiert und dauert nur 30 Sekunden in der ersten Schleife.

Bearbeiten: Ich habe die Lösung gefunden.

Statt:

URL url = new URL("file://C:/jtest/");

habe ich es zu:

URL url = new URL("file:/C:/jtest/");

Jetzt funktioniert es perfekt. Ich weiß nicht, warum es funktioniert, aber ich sehe nicht, wie ich (und 5 andere Leute) das übersehen hätte. Jetzt fühle ich mich dumm ..

+1

loadClass überprüft zuerst den Cache, um zu sehen, ob die Klasse bereits geladen ist, wenn ich mich richtig erinnere. Das würde erklären, warum es bei der zweiten Iteration nicht lange dauert. –

+0

Ist das echte "Foo" in einem Paket? Das Laden aus dem Standardpaket (kein Paket) kann seltsame Auswirkungen haben. Versuche, zu foo.Foo zu wechseln. – flicken

+0

Das ist interessant ... neue URL ("file:/C:/jtest /"). GetPath() ist/C:/jtest /. Ich frage mich, wie URLClassLoader das interpretiert. –

Antwort

5

Nach 30 Sekunden sollten Sie in der Lage sein, Ihren Code zu „Profil“ und genau sehen, wo das Problem liegt (beim Laden der Klasse? In die Erstellung die Instanz? In die Methode aufzuzublicken? Etc?)

Da es 30 Sekunden dauert (und nicht etwas viel kleiner, in der Größenordnung von 10ms oder so), können Sie einfach System.out.println(new Date()); zwischen jeder Zeile Ihres Codes verwenden.

Ich vermute, dass Sie finden, dass es der Lader ist.loadClass (String) dauert so lange - und ich vermute, dass Sie entweder einen sehr langen Klassenpfad oder einen Klassenpfad mit einer Netzwerkressource haben.

+2

@unknown: In diesem Fall (und wenn Sie das Problem gelöst haben) sollten Sie die Antwort, die Sie wissen, korrekt und akzeptieren Sie diese. –

5

Überprüfen Sie Ihren Standard-Klassenpfad. Vielleicht bezieht es sich auf eine nicht verfügbare Netzwerkfreigabe oder etwas ähnliches.

+0

Wie mache ich das? – Lucky

+0

echo% CLASSPATH% in einer Eingabeaufforderung –

+0

oder überprüfen Sie die Einstellungen in Eclipse –

2

Es ist nichts falsch mit Ihrem Code ... Ich konnte Ihr Programm in weniger als einer Sekunde kompilieren. Ich betreibe Java 1.6.11 auf Vista Business.

Vielleicht ist Ihre public string doit(string arg) Methode, was so lange dauert. Können Sie versuchen, es ohne Reflektion aufzurufen, um zu sehen, ob es lange dauert? Versuchen Sie, doit() zu haben, geben Sie einfach den übergebenen Parameter zurück (anstatt die Zeichen umzukehren), um zu sehen, ob Ihr Umkehrungsalgorithmus das verlangsamt.

+0

Er sagte, es dauerte 30 Sekunden, um abzuschließen. Ich nehme an, dies bedeutet, die Ausführung abzuschließen, nicht zu kompilieren. –

+0

Es kompiliert und lief in weniger als einer Sekunde. Deshalb habe ich ihm gesagt, er solle versuchen, es mit einer anderen Implementierung seiner doit() - Methode auszuführen. – Cuga

+0

doit() kehrt sofort zurück, wenn kein Reflexionscode verwendet wird. – Lucky

3

Es ist möglich, dass beim Erstellen der Instanz viele andere Klassen geladen werden, von denen einige über statische Initialisierer verfügen, die lange dauern. Setzen Sie diesen Code in eine Schleife und sehen Sie, was nachfolgende Objekt-Kreationen kosten.

Bearbeiten: Cool, basierend auf Ihrem Profiling klingt das wie Sie sind nah an der Ursache. Eine andere Sache zu berücksichtigen ist, wenn Sie Bibliotheken verwenden, die große Startkosten haben. Ich benutze iBatis, um unsere Interaktionen mit Oracle zu verwalten, und wenn es zuerst hochfährt, liest es eine Reihe von XML-Dateien ein und verarbeitet die Abfragemuster in ihnen. Es gibt eine bemerkbare Verzögerung. Es wäre interessant zu hören, was Sie herausgefunden haben, aber Sie könnten entscheiden, dass die Kosten für eine Zeit tolerierbar sind.

2

Ist es möglich, dass Sie Foo in den Standard-CLASSPATH zu setzen, so dass Sie nur so etwas wie verwenden:

ClassLoaderTest.class.getClassLoader() 

, wo ClassLoaderTest ist die Klasse, die Sie in oder laufen lassen, zur Verfügung gestellt, wenn Sie. nicht in einem statischen Kontext ausgeführt wird, dann:

this.getClass().getClassLoader()` 

Jede dieser sparen Sie einen neuen Klassenlader Instanziierung.

Aber ich weiß nicht, ob dir das hilft (du musst dich profilieren), und die Reflektion wird immer merklich langsamer sein. Das kommt nicht herum. Natürlich sollte es aus diesem und anderen Gründen minimal in der Produktion verwendet werden.

EDIT: Da loadClass die meiste Zeit dauert, könnte es sein, dass C: \ jtest überladen ist. Sie können versuchen, Foo.class in ein Verzeichnis zu legen und dieses als URL zu verwenden. Der Grund, warum es beim zweiten Mal schneller ist, ist, dass Foo bereits geladen ist.

6

Für Ihre Info: Vielleicht ein bisschen zu spät für Sie, aber ich stolperte über das gleiche Problem und fand diesen Beitrag.

Es sieht so aus, als würde // eine Remote-Suche erzwingen, wenn Sie mit -verbose: class laufen, wird eine UnknownHostException geladen, diese muss intern beim Laden der Klasse ausgelöst werden.

Ich habe versucht, die folgenden:

URL url = new URL ("file: // localhost/C:/jtest /");

und das funktioniert (fast) so schnell wie Ihre Single-Slash-Lösung.

+0

Sparte mir 4 Minuten Laufzeit und Stunden Haare ziehen – MonoThreaded