views:

192

answers:

3

I was using newInstance() in a sort-of performance-critical area of my code. The method signature is:

<T extends SomethingElse> T create(Class<T> clasz)

I pass Something.class as argument, I get an instance of SomethingElse, created with newInstance().

Today I got back to clear this performance TODO from the list, so I ran a couple of tests of new operator versus newInstance(). I was very surprised with the performance penalty of newInstance().

I wrote a little about it, here: http://bruno.linker45.eu/2010/07/20/new-vs-newinstance/

(Sorry about the self promotion... I'd place the text here, but this question would grow out of proportions.)

What I'd love to know is why does the -server flag provide such a performance boost when the number of objects being created grows largely and not for "low" values, say, 100 or 1000.

I did learn my lesson with the whole reflections thing, this is just curiosity about the optimisations the JVM performs in runtime, especially with the -server flag. Also, if I'm doing something wrong in the test, I'd appreciate your feedback!


Edit: I've added a warmup phase and the results are now more stable. Thanks for the input!

+1  A: 

Among other things, the garbage collection profile for the -server option has significantly different survivor space sizing defaults.

On closer reading, I see that your example is a micro-benchmark and the results may be counter-intuitive. For example, on my platform, repeated calls to newInstance() are effectively optimized away during repeated runs, making newInstance() appear 12.5 times faster than new.

trashgod
I ran with -XX:+PrintGCDetails and I saw only 1 collection - the full collection I forced between both tests. I made sure to provide enough memory to the app so that it wouldn't need to resize or collect (actually I was also preventing resize by using same -Xmx as -Xms).Under these conditions, I believe GC wasn't a decisive factor in the results.
brunodecarvalho
@brunodecarvalho: Yes, I see what you mean, and I've elaborated above. You may need to rewrite your benchmark to get meaningful results.
trashgod
Yeah, I was afraid of the micro-benchmark effect here. I'm only adding the new instance to that object array in the hopes that the compiler wouldn't realise it's a pointless operation and delete the whole loop altogether. I'll test this again using the system I was about to optimise.However, I find a bit odd that newInstance() gets faster than new. Looking through JDK's source, seems there is a lot of overhead that newInstance() goes through and new doesn't.
brunodecarvalho
@brunodecarvalho: I think a trivial constructor gets optimized away, while a non-trivial constructor comes to dominate the time for either.
trashgod
+2  A: 

I did learn my lesson with the whole reflections thing, this is just curiosity about the optimisations the JVM performs in runtime, especially with the -server flag. Also, if I'm doing something wrong in the test, I'd appreciate your feedback!

Answering the second part first, your code seems to be making the classic mistake for Java micro-benchmarks and not "warming up" the JVM before making your measurements. Your application needs to run the method that does the test a few times, ignoring the first few iterations ... at least until the numbers stabilize. The reason for this is that a JVM has to do a lot of work to get an application started; e.g. loading classes and (when they've run a few times) JIT compiling the methods where significant application time is being spent.

I think the reason that "-server" is making a difference is that (among other things) it changes the rules that determine when to JIT compile. The assumption is that for a "server" it is better to JIT sooner this gives slower startup but better throughput. (By contrast a "client" is tuned to defer JIT compiling so that the user gets a working GUI sooner.)

Stephen C
I've added a couple of warmup iterations before I ran the actual tests and I'm getting better, more stable figures. With -server flag, newInstance() stabilizes at 2.5~2.9 times slower than new (both for 100 and for 1kk objects). Without the -server it still goes as high as 8~9 times slower for 100 objects up to 25 slower for 1kk).Great advice! :)
brunodecarvalho
+1  A: 

IMHO the performance penalty comes from the class loading mechanism. In case of reflection all the security mechanism are used and thus the creation penalty is higher. In case of new operator the classes are already loaded in VM (checked and prepared by the default classloader) and the actual instantiation is a cheap process. The -server parameter does a lot of JIT optimizations for the frequently used code. You might want to try it also with -batch parameter that will trade off the startup-time but then the code will run faster.

Daniel Voina
Your explanation makes sense, as I've looked through the code and figured the whole security mechanism would add a significant overhead. However, the -batch flag doesn't seem to be valid.
brunodecarvalho
@brunodecarvalho: You are right! The syntax is -Xbatch not batch as I have typed in. For details see: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/tooldocs/windows/java.html and http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/technotes/tools/windows/java.html
Daniel Voina
Didn't seem to do much difference. I removed the warmup phase to see if it would affect the results but I'm getting pretty much the same values with -Xbatch and without. Still, that's a very interesting flag!
brunodecarvalho