Java Reflection Performance
Asked Answered
P

14

201

Does creating an object using reflection rather than calling the class constructor result in any significant performance differences?

Paraldehyde answered 12/1, 2009 at 14:25 Comment(1)
Related: Any way to further optimize Java reflective method invocation?Postprandial
C
201

Yes - absolutely. Looking up a class via reflection is, by magnitude, more expensive.

Quoting Java's documentation on reflection:

Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

Here's a simple test I hacked up in 5 minutes on my machine, running Sun JRE 6u10:

public class Main {

    public static void main(String[] args) throws Exception
    {
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = new A();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void doReflection() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = (A) Class.forName("misc.A").newInstance();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }
}

With these results:

35 // no reflection
465 // using reflection

Bear in mind the lookup and the instantiation are done together, and in some cases the lookup can be refactored away, but this is just a basic example.

Even if you just instantiate, you still get a performance hit:

30 // no reflection
47 // reflection using one lookup, only instantiating

Again, YMMV.

Ciracirca answered 12/1, 2009 at 14:30 Comment(14)
On my machine the .newInstance() call with only one Class.forName() call scores 30 or so. Depending on VM version, the difference may be closer than you think with an appropriate caching strategy.Seger
On my system (a dual core 2.4 GHz), I see times of - Instantiate: 7.8, Reflect 1: 171.9, and Reflect 2: 3181.3 (where reflect 2 is the reflection as above, and 1 is the reflection with forName done only once - all times are for 1,000,000 iterations).Fukien
Also, this performance factor depends very much on the cost of the object initialization - i.e. the work done in the constructor.Fukien
Generally you would use it to instantiate an object once and hold onto the instance. It wouldn't be a good idea for something you do 100 times or more a second, but then when you are doing anything more than 100x a second you should pay attention to it. Once you've instantiated the object, using it is no worse (unless you are accessing the methods via reflection as well). I'm just glad reflection is hard enough to scare away many people. Ruby it's really easy... makes it very heavily used--and makes me a little nervous.Grath
@Peter Lawrey below pointed out that this test was completely invalid because the compiler was optimizing out the non-reflective solution (It can even prove that nothing is done and optimize out the for loop). Needs to be re-worked and should probably be removed from S.O. as bad / misleading information. Cache the created objects in an array in both cases to prevent the optimizer from optimizing it out. (It can't do this in the reflective situation because it can't prove that the constructor doesn't have side-effects)Grath
@Bill K - let's not get carried away. Yes, the numbers are off due to optimizations. No, the test is not completely invalid. I added a call that removes any possibility of skewing the result, and the numbers are still stacked against reflection. In any case, remember that this is a very crude micro-benchmark which just shows that reflection always incurs a certain overheadCiracirca
@YuvalAdam Of course it incurs an overhead that someone should be aware of if doing anything inside a loop of a million iterations or more, of course at that point you must take into consideration the evils of object instantiation and string contatenation as well--and none of these should be considered until we are doing them inside a loop of a million or more (and probably not even then!)Grath
This is probably a useless benchmark. Depending on what doSomething does. If it does nothing with visible side effect, then your benchmark runs only dead code.Chaotic
Benchmark is not taking into consideration JVM warmup / compilation phase. Wrap main's content in a loop, and you'll see completely different numbers on subsequent runs.Morrismorrison
I just witnessed the JVM optimizing reflection 35 fold. Running the test repeatedly in a loop is how you test optimized code. First iteration: 3045ms, second iteration: 2941ms, third iteration: 90ms, fourth iteration: 83ms. Code: c.newInstance(i). c is a Constructor. Non reflective code: new A(i), which yields 13, 4, 3.. ms times. So yes, reflection in this case was slow, but not nearly as much slower as what people are concluding, because every test I'm seeing, they're simply running the test once without giving the JVM the opportunity to replace byte codes with machine code.Morrismorrison
@Mike, according to your numbers, Reflection is even worse than it comes off in Yuvals test - instead of a factor of 13x slower, your initial numbers are 234x slower, and instead of 1.6x slower for optimized, your numbers indicate that reflection is 27x(!) slower. In other words, according to your numbers, it's not only "nearly as much slower than what people are concluding", it's MUCH MUCH slower than what people are concluding.Astigmatic
@Torque, that could be. After all, the re-compiled "warmed up" code benefits the non-reflection version much more. With reflection, it spends most of its time inside the libraries.Morrismorrison
Note that System.currentTimeMillis() cannot be used for a benchmark like this because of its common granularity in the order of 10-20ms! System.nanoTime() should be used, or the loop count increased by a factor of 100 or more.Prescribe
use JMH for all your microbenchmarks, i suggest to remove your benchmark code here because it is misleadingTactual
G
105

Yes, it's slower.

But remember the damn #1 rule--PREMATURE OPTIMIZATION IS THE ROOT OF ALL EVIL

(Well, may be tied with #1 for DRY)

I swear, if someone came up to me at work and asked me this I'd be very watchful over their code for the next few months.

You must never optimize until you are sure you need it, until then, just write good, readable code.

Oh, and I don't mean write stupid code either. Just be thinking about the cleanest way you can possibly do it--no copy and paste, etc. (Still be wary of stuff like inner loops and using the collection that best fits your need--Ignoring these isn't "unoptimized" programming, it's "bad" programming)

It freaks me out when I hear questions like this, but then I forget that everyone has to go through learning all the rules themselves before they really get it. You'll get it after you've spent a man-month debugging something someone "Optimized".

EDIT:

An interesting thing happened in this thread. Check the #1 answer, it's an example of how powerful the compiler is at optimizing things. The test is completely invalid because the non-reflective instantiation can be completely factored out.

Lesson? Don't EVER optimize until you've written a clean, neatly coded solution and proven it to be too slow.

Grath answered 13/2, 2009 at 22:50 Comment(16)
I totally agree with the sentiment of this response, however if you're about to embark upon a major design decision it helps to have an idea about performance so you don't go off on a totally unworkable path. Maybe he's just doing due diligence?Zoie
-1: Avoiding doing things the wrong way is not optimisation, it is just doing things. Optimisation is doing things the wrong, complicated way because of either real or imaginary performance concerns.Igor
@Igor totally agree. Choosing a linked list over an array list for an insertion sort is simply the right way to do things. But this particular question--there are good use cases for both sides of the original question, so choosing one based on performance rather than the most usable solution would be wrong. I'm not sure we are disagreeing at all, so I'm not sure why you said "-1".Grath
Because you shouldn't mention performance as an issue at all until it has been established as the right thing to do. In this context, constructor for objects inside your type-checking/compilation 'world', reflection for things outside it. Performance is like price, just because something is expensive doesn't in itself make it better. A $10 digital watch probably tells the time as well or better than a $30,000 platinum chunk of bling.Igor
@Igor that's exactly what I said. Still not sure why you feel we are disagreeing. Did you possibly misread my post? "You must never optimize until you are sure you need it" == "you shouldn't mention performance as an issue at all until it has been established as the right thing to do."Grath
Any sensible analyst programmers needs to consider efficiency at an early stage or you might end up with a system that can NOT be optimised in an efficient and costworthy timeframe. No, you dont optimise every clock cycle but you most certainly DO employ best practices for something as basic as class instantiation. This example is a great one of WHY you consider such questions regarding reflection. It would be a pretty poor programmer who went ahead and used reflection throughout a million line system only to later discover it was orders of magnitude too slow.Bria
@Richard Riley Generally class instantiation is a pretty rare event for the selected classes you will use reflection on. I suppose you are right though--some people might instantiate every class reflectively, even ones that are recreated constantly. I would call that pretty bad programming (although even then you COULD implement a cache of class instances for reuse after the fact and not harm your code too much--so I guess I'd still say ALWAYS design for readability, then profile and optimize later)Grath
-1: Considering the simplicity and genericity of the question, this answer is just noise... Even if I share your sentiments :)Amasa
@LukasEder As with the questions "What is the best way to commit suicide" or "How do I best aim this bow to shoot the apple off my sister's head", the direct answer does not solve the underlying problem exposed by the question's existence and may in all likelyhood exasperate it--possibly resulting in someone getting hurt!Grath
Yes, avoid premature optimization, but that doesn't answer the question. There are definitely times when the answer matters.Gnathic
@Christopher, didn't "Yes it's slower" answer the question? I gave the answer all the significance I thought it deserved--I put it at the top and made it brief in order to enhance the real point that if you are thinking about optimization in this way you are almost certainly Doing It Wrong--but I did answer...Grath
Disagree partially. Frequently it is necessary to think of performance at design time, the world is full off bloaty java APIs/Standards which perform poorly because of following this rule. Some things can't be optimized that easy at late stages of a project.Tactics
@user1870904 Also, all I suggested was code it well, unit test it for performance and then fix it if it performs below some set bar. I never said leave it until the end of the project or ignore it altogether. The first pass through, coding it as clearly as you can, will improve your optomized code and give you known good unit tests. You really have a problem with this?Grath
The full quotation is"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified" - Donald KnuthSpecimen
@Specimen I believe that is nearly exactly what I was saying--I rephrased the rest but left out none of the sentament of the original quote.Grath
quite right, to worry about performance during coding time makes more trouble then solving.Nam
S
40

You may find that A a = new A() is being optimised out by the JVM. If you put the objects into an array, they don't perform so well. ;) The following prints...

new A(), 141 ns
A.class.newInstance(), 266 ns
new A(), 103 ns
A.class.newInstance(), 261 ns

public class Run {
    private static final int RUNS = 3000000;

    public static class A {
    }

    public static void main(String[] args) throws Exception {
        doRegular();
        doReflection();
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = new A();
        }
        System.out.printf("new A(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }

    public static void doReflection() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = A.class.newInstance();
        }
        System.out.printf("A.class.newInstance(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }
}

This suggest the difference is about 150 ns on my machine.

Selves answered 13/2, 2009 at 21:53 Comment(11)
so you've just killed the optimiser, so now both versions are slow. Reflection is, therefore, still damn slow.Greyson
@Greyson if the optimizer was optimizing out the creation itself then it wasn't a valid test. @Peter's test is therefore valid because it actually compares the creation times (The optimizer wouldn't be able to work in ANY real-world situation because in any real-world situation you need the objects you are instantiating).Grath
@Chaotic In which case you could have taken the opportunity to create a better benchmark. Perhaps you can offer something constructive, like what should be in the body of the method.Selves
on my mac, openjdk 7u4, the difference is 95ns versus 100ns. Instead of storing A's in the array, I store hashCodes. If you say -verbose:class you can see when hotspot generates bytecode for constructing A and the accompanying speedup.Heifetz
@PeterLawrey If I lookup once (one call toClass.getDeclaredMethod) and then call Method.invoke multiple times? Am I using reflection once or as many times as I invoke it? Follow up question, what if instead of Method it is a Constructor and I do Constructor.newInstance multiple times?Codycoe
@tMJ you are using reflection as many times as you call it. As the JVM warms up this will get more efficient over time but the reflection is still there.Selves
Then why the time difference, in @Yuval 's answer above, in case of one lookup and multiple use quite lower than ~150ns? Or is he again hitting some sort of optimization (I'm guessing not)?Codycoe
@tMJ 150 ns sounds like a long time. A simple method call takes about 50 ns and after inlining it might not take any time at all. I would hope reflaction is not that slow for simple cases at least.Selves
@tMJ to give you some context, I am working if a persistence library which takes 75 ns to read/write a small indexed message. You can do a lot in that amount if time.Selves
Just for fun I run your "test" on an Android nexus 9. I wasn't expecting this : new A(), 146 ns A.class.newInstance(), 1 356 ns new A(), 187 ns A.class.newInstance(), 1 374 nsSupposal
@Supposal some aspects of the JIT on Android are not as heavily optimised.Selves
C
33

If there really is need for something faster than reflection, and it's not just a premature optimization, then bytecode generation with ASM or a higher level library is an option. Generating the bytecode the first time is slower than just using reflection, but once the bytecode has been generated, it is as fast as normal Java code and will be optimized by the JIT compiler.

Some examples of applications which use code generation:

  • Invoking methods on proxies generated by CGLIB is slightly faster than Java's dynamic proxies, because CGLIB generates bytecode for its proxies, but dynamic proxies use only reflection (I measured CGLIB to be about 10x faster in method calls, but creating the proxies was slower).

  • JSerial generates bytecode for reading/writing the fields of serialized objects, instead of using reflection. There are some benchmarks on JSerial's site.

  • I'm not 100% sure (and I don't feel like reading the source now), but I think Guice generates bytecode to do dependency injection. Correct me if I'm wrong.

Compte answered 13/2, 2009 at 23:15 Comment(0)
D
28

"Significant" is entirely dependent on context.

If you're using reflection to create a single handler object based on some configuration file, and then spending the rest of your time running database queries, then it's insignificant. If you're creating large numbers of objects via reflection in a tight loop, then yes, it's significant.

In general, design flexibility (where needed!) should drive your use of reflection, not performance. However, to determine whether performance is an issue, you need to profile rather than get arbitrary responses from a discussion forum.

Duston answered 12/1, 2009 at 15:36 Comment(0)
J
24

There is some overhead with reflection, but it's a lot smaller on modern VMs than it used to be.

If you're using reflection to create every simple object in your program then something is wrong. Using it occasionally, when you have good reason, shouldn't be a problem at all.

Jardine answered 12/1, 2009 at 14:30 Comment(0)
E
13

Yes there is a performance hit when using Reflection but a possible workaround for optimization is caching the method:

  Method md = null;     // Call while looking up the method at each iteration.
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md = ri.getClass( ).getMethod("getValue", null);
        md.invoke(ri, null);
      }

      System.out.println("Calling method " + CALL_AMOUNT+ " times reflexively with lookup took " + (System.currentTimeMillis( ) - millis) + " millis");



      // Call using a cache of the method.

      md = ri.getClass( ).getMethod("getValue", null);
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md.invoke(ri, null);
      }
      System.out.println("Calling method " + CALL_AMOUNT + " times reflexively with cache took " + (System.currentTimeMillis( ) - millis) + " millis");

will result in:

[java] Calling method 1000000 times reflexively with lookup took 5618 millis

[java] Calling method 1000000 times reflexively with cache took 270 millis

Elenore answered 20/6, 2013 at 1:28 Comment(1)
Reusing method/constructor is indeed useful and helps, but note that test above does not give meaningful numbers due to the usual benchmarking problems (no warmup, so first loop in particular is mostly measuring JVM/JIT warmup time).Duodenum
H
11

Interestingly enough, settting setAccessible(true), which skips the security checks, has a 20% reduction in cost.

Without setAccessible(true)

new A(), 70 ns
A.class.newInstance(), 214 ns
new A(), 84 ns
A.class.newInstance(), 229 ns

With setAccessible(true)

new A(), 69 ns
A.class.newInstance(), 159 ns
new A(), 85 ns
A.class.newInstance(), 171 ns
Higgle answered 27/10, 2011 at 14:53 Comment(2)
Seems obvious to me in principle. Do these numbers scale linearly, when running 1000000 invocations?Amasa
Actually setAccessible() can have much more difference in general, especially for methods with multiple arguments, so it should always be called.Duodenum
J
8

Reflection is slow, though object allocation is not as hopeless as other aspects of reflection. Achieving equivalent performance with reflection-based instantiation requires you to write your code so the jit can tell which class is being instantiated. If the identity of the class can't be determined, then the allocation code can't be inlined. Worse, escape analysis fails, and the object can't be stack-allocated. If you're lucky, the JVM's run-time profiling may come to the rescue if this code gets hot, and may determine dynamically which class predominates and may optimize for that one.

Be aware the microbenchmarks in this thread are deeply flawed, so take them with a grain of salt. The least flawed by far is Peter Lawrey's: it does warmup runs to get the methods jitted, and it (consciously) defeats escape analysis to ensure the allocations are actually occurring. Even that one has its problems, though: for example, the tremendous number of array stores can be expected to defeat caches and store buffers, so this will wind up being mostly a memory benchmark if your allocations are very fast. (Kudos to Peter on getting the conclusion right though: that the difference is "150ns" rather than "2.5x". I suspect he does this kind of thing for a living.)

Jaco answered 11/7, 2011 at 19:37 Comment(0)
M
7

Yes, it is significantly slower. We were running some code that did that, and while I don't have the metrics available at the moment, the end result was that we had to refactor that code to not use reflection. If you know what the class is, just call the constructor directly.

Manganous answered 12/1, 2009 at 14:29 Comment(2)
+1 I've had a similar experience. It's good to make sure to only use reflection if it's absolutely necessary.Posner
e.g. AOP based libraries do need reflection.Pannonia
A
4

In the doReflection() is the overhead because of Class.forName("misc.A") (that would require a class lookup, potentially scanning the class path on the filsystem), rather than the newInstance() called on the class. I am wondering what the stats would look like if the Class.forName("misc.A") is done only once outside the for-loop, it doesn't really have to be done for every invocation of the loop.

Aphotic answered 6/9, 2011 at 21:50 Comment(0)
B
1

Yes, always will be slower create an object by reflection because the JVM cannot optimize the code on compilation time. See the Sun/Java Reflection tutorials for more details.

See this simple test:

public class TestSpeed {
    public static void main(String[] args) {
        long startTime = System.nanoTime();
        Object instance = new TestSpeed();
        long endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");

        startTime = System.nanoTime();
        try {
            Object reflectionInstance = Class.forName("TestSpeed").newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");
    }
}
Brno answered 12/1, 2009 at 14:38 Comment(2)
Note that you should separate the lookup (Class.forName()) from the instanciation (newInstance()), because they vary significantly in their performance characteristics and you can occasionally avoid the repeated lookup in a well-designed system.Craal
Also: you need to execute each task many, many times to get a useful benchmark: first of all the actions are too slow to be measured reliably and secondly you'll need to warm up the HotSpot VM to get useful numbers.Craal
G
1

Often you can use Apache commons BeanUtils or PropertyUtils which introspection (basically they cache the meta data about the classes so they don't always need to use reflection).

Godolphin answered 13/2, 2009 at 17:8 Comment(0)
K
0

I think it depends on how light/heavy the target method is. if the target method is very light(e.g. getter/setter), It could be 1 ~ 3 times slower. if the target method takes about 1 millisecond or above, then the performance will be very close. here is the test I did with Java 8 and reflectasm :

public class ReflectionTest extends TestCase {    
    @Test
    public void test_perf() {
        Profiler.run(3, 100000, 3, "m_01 by refelct", () -> Reflection.on(X.class)._new().invoke("m_01")).printResult();    
        Profiler.run(3, 100000, 3, "m_01 direct call", () -> new X().m_01()).printResult();    
        Profiler.run(3, 100000, 3, "m_02 by refelct", () -> Reflection.on(X.class)._new().invoke("m_02")).printResult();    
        Profiler.run(3, 100000, 3, "m_02 direct call", () -> new X().m_02()).printResult();    
        Profiler.run(3, 100000, 3, "m_11 by refelct", () -> Reflection.on(X.class)._new().invoke("m_11")).printResult();    
        Profiler.run(3, 100000, 3, "m_11 direct call", () -> X.m_11()).printResult();    
        Profiler.run(3, 100000, 3, "m_12 by refelct", () -> Reflection.on(X.class)._new().invoke("m_12")).printResult();    
        Profiler.run(3, 100000, 3, "m_12 direct call", () -> X.m_12()).printResult();
    }

    public static class X {
        public long m_01() {
            return m_11();
        }    
        public long m_02() {
            return m_12();
        }    
        public static long m_11() {
            long sum = IntStream.range(0, 10).sum();
            assertEquals(45, sum);
            return sum;
        }    
        public static long m_12() {
            long sum = IntStream.range(0, 10000).sum();
            assertEquals(49995000, sum);
            return sum;
        }
    }
}

The complete test code is available at GitHub:ReflectionTest.java

Kendy answered 10/1, 2017 at 4:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.