views:

533

answers:

6

Hi, I'm trying to load a java .class file dynamically and call it by reflection.

I've got a class called Foo; it has an empty constructor and has one method called doit() which takes a String argument and returns a String. Also it reverses the String.

Here is my 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);

As expected this prints "Hello World!". However, it takes about 30 seconds to complete. I know reflection is slow, but I expect it to be 10 ms or something.

I'm using Eclipse with JRE 1.6.0_13, and I'm running Windows Vista.

What am I doing wrong here?

Thanks.

Edit: I've profiled the code, and all of its time is used in the third line(loadClass()). Everything else happens instantly.

Edit: I've put the code in a loop; the slow function somehow gets optimized and takes 30 seconds only on the first loop.

Edit: I've found the solution.

Instead of:

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

I changed it to:

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

Now it works perfectly. I don't know why it works, but I don't see how I (and 5 other people) could have missed that. Now I feel dumb..

+4  A: 

Check your default classpath. Maybe it refers to an unavailable network share or something like that.

ammoQ
How do I do that?
unknown
echo %CLASSPATH%in a command prompt
ammoQ
or check the settings in Eclipse
ammoQ
+2  A: 

There's nothing wrong with your code... I was able to compile your program in less than a second. I'm running java 1.6.11 on Vista Business.

Perhaps your public string doit(string arg) method is what's taking so long. Can you try invoking it without using reflection to see if it takes a long time? Try having doit() simply return the parameter you pass in (rather than reverse the characters) to see if your reversal algorithm is what's slowing it down.

Cuga
He said it took 30 seconds to complete. I assume this means to complete execution, not to compile.
Matthew Flaschen
It compiled and ran in less than a second. That's why I told him to try executing it with a different implementation of his doit() method.
Cuga
doit() returns immediately when using non-reflection code.
unknown
+5  A: 

At 30 seconds, you should be able to "profile" your code and see exactly where the problem lies (in loading of the class? in creating the instance? in looking up the method? etc?)

Since it's taking 30 seconds (and not something much smaller, on the order of 10ms or so), you can just use System.out.println(new Date()); between each line of your code.

I suspect you'll find it's the loader.loadClass(String) taking so long - and I suspect that you'll find you either have a very long classpath, or a classpath that includes a network resource of some sort.

Jared
Accepting this because no satisfactory answer exists.
unknown
@unknown: in that case (and if you solved the problem) you should post the answer that you know is correct and accept that one.
Joachim Sauer
+3  A: 

It's possible that when you create the instance it causes many other classes to load, some of which have static initializers that do stuff that takes a long time. Put this code in a loop and see what subsequent object creations cost.

Edit: Cool, based on your profiling this sounds like you're getting close to the root cause. Another thing to consider is if you are using libraries that have big startup costs. I use iBatis to manage our interactions with Oracle, and when it first fires up it reads in a bunch of XML files and processes the query patterns in them. There's a noticable lag. It'd be interesting to hear what you find out, but you might decide the one time cost is tolerable.

Jim Ferrans
+2  A: 

Is it possible for you to put Foo in the default CLASSPATH so you can just use something like:

ClassLoaderTest.class.getClassLoader()

, where ClassLoaderTest is the class you're running in. Or, provided if you're not running in a static context, then:

this.getClass().getClassLoader()`

Either of these will save you instantiating a new class loader.

But I don't know if this will even help you (you have to profile), and reflection is always going to be noticeably slower. There's no getting around that. Of course, it should be used minimally in production, for this and other reasons.

EDIT: Since loadClass is taking most of the time, it could be that C:\jtest is cluttered. You can try putting Foo.class alone in a directory and using that as the URL. Of course, the reason it is faster the second time is that Foo is already loaded.

Matthew Flaschen
+2  A: 

For your info: Maybe a bit late for you, but I stumbled upon the same issue and found this post.

It looks like the // is forcing a remote search, if you run with -verbose:class there is a UnknownHostException loaded so this must be thrown internally during class loading.

I tried the following:

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

and this works (almost) as quickly as your single slash solution.

Bert.V