15

So I know this topic has been done before, e.g. Java Reflection Performance, but my particular question is, it seems that many popular libraries are implemented via annotations and reflection (Gson, Jackson, Jaxb implementations, hibernate search, for example). Many (if not all) libraries provide good (or great) performance even though they use reflection. My question is, how do they do it? Is there some "tricks" to know or do they simply use straight reflection, and the performances worries are overblown?

EDIT: So for example, when we write: MyObject obj = new Gson().fromJson(someInputStream, MyObject.class);

I can understand how the library might internally cache the Field objects, but it seems to me that it needs to reflectively instantiate the object each time, and it needs to call the setter on each field (reflectively) based on the parsed value from the json. Or is there some way to pay (all) the cost of reflection only at startup?

I've definitely noticed that Gson/Jackson etc. have relatively large startup cost and are super fast after that. So obviously I'm wondering, if I write a library that does something vaguely similar, are there tricks I need to know about? Because it appears you can't get away from some amount of reflection, on each call.

Community
  • 1
  • 1
Kevin
  • 24,871
  • 19
  • 102
  • 158
  • possible duplicate of [Hibernate implementation. Are we paying the reflection penalty?](http://stackoverflow.com/questions/547899/hibernate-implementation-are-we-paying-the-reflection-penalty) – OscarRyz Mar 10 '11 at 19:40

6 Answers6

10

What is costly is the method lookup, but method invocation once is very similar.

So, once you found what method to invoke, you just keep a reference to it, and consecutive invocations works similarly.

Of course there are situations where you want to reduce every millisecond.

Although you should beaware of micro benchmarks you could try this just to get a rough idea:

import java.lang.reflect.*;
class ReflectionOrNot { 
    public void run() { 
        try { 
            Thread.currentThread().sleep( 0 );
        } catch( InterruptedException ie ){}
    }

    public static void main( String ... args ) throws Exception { 

        ReflectionOrNot ron = new ReflectionOrNot();
        int max = 1000000;

        long start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
            ron.run();
        }
        System.out.println( "Direct access took: " + ( System.currentTimeMillis() - start ) );


        Method m = ReflectionOrNot.class.getDeclaredMethod("run");
        start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
            m.invoke( ron );
        }
        System.out.println( "Reflection    Took: " + ( System.currentTimeMillis() - start ) );


        start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
             m = ReflectionOrNot.class.getDeclaredMethod("run");
            m.invoke( ron );
        }
        System.out.println( "Lookup + Reflect  : " + ( System.currentTimeMillis() - start ) );


    }
}

Calling 1 million times with the different approaches gave me:

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 422
Reflection    Took: 1156
Lookup + Reflect  : 3016

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 422
Reflection    Took: 1125
Lookup + Reflect  : 2750

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 485
Reflection    Took: 1203
Lookup + Reflect  : 2797
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • `Class#getDeclaredMethod`(`s`) **copies** its result, unnecessarily slowing down the execution time where the result it used *read-only* – Binkan Salaryman Mar 11 '15 at 13:12
4

There are generally no tricks. Most reflection based actions are performed on application startup, thereby not affecting the run-time performance after initiation. A perfect example of this are the entire hibernate-annotation API.

Sometimes the presence of annotations needs to affect the application throughout its entire lifecycle. This is usually configured using dynamic proxies (or e.g., cglib proxies when proxying concrete clases) or interceptors based on the initial reflection readings.

Johan Sjöberg
  • 47,929
  • 21
  • 130
  • 148
  • Sjoberg: please see my edit to my question above. Maybe you've answered my question with your comment about dynamic proxies (cglib I assume) but I don't see exactly how that would work? Maybe you can elaborate or link to an existing description? – Kevin Mar 10 '11 at 19:53
  • @Kevin, A dynamic proxy is *just* like a regular proxy (only *runtime*) - it intercepts method calls and does something with it. For instance springs `@Transactional` annotation creates a proxy which creates a transaction around the target method. – Johan Sjöberg Mar 10 '11 at 20:04
  • Sjoberg: Ok, so this doesn't explain how a library like Jackson or Gson avoids recurring runtime costs (e.g. in my example above Gson needs to instantiate a new object each time, then call a setter (reflectively) for each property it finds in some json document). Other serialization type libraries are going to face the same issue. – Kevin Mar 10 '11 at 21:18
  • @Kevin, converting e.g., xml-to-objects doesn't necessarily need reflection at all. Even if it would, the cost of reflection is quite *small* in contrast to e.g., sending traffic over the web. Again to my first idiom, there generally are no tricks because reflection isn't that slow. In some cases byte-code modification is used (e.g., hibernate uses `cglib` for lazy-loading attributes). – Johan Sjöberg Mar 11 '11 at 08:13
  • @Kevin, see for instance [Benchmarking cost of dynamic proxies](http://ordinaryjava.blogspot.com/2008/08/benchmarking-cost-of-dynamic-proxies.html). He claims using proxy is *roughly* 1.6 times slower; 1.6 times slower than almost zero is still almost zero. `cglib` is likely just as performant and allows you to inject desired bytecode as a complement to regular reflection. – Johan Sjöberg Mar 11 '11 at 08:27
2

Reflection is slow compared to using data placed in the application by the compiler, but fast compared to providing the same data from say a database.

As long as the information that the application gets through reflection is retrieved on-demand and stored in a local cache (or in the form of initialised objects) so that retrieval is a one-time event during the lifecycle of the application you don't need to worry about reflection being a performance bottleneck.

rsp
  • 23,135
  • 6
  • 55
  • 69
1

The trick is to use reflection at "configuration-time", not at "run-time".

Use reflection to inspect whatever is needed and then store (in memory) this information for use at runtime.

Peter Knego
  • 79,991
  • 11
  • 123
  • 154
0

I created a benchmark with one million iterations and ten fields filled with random numbers in Java 8 on a Windows Laptop. Here are the results:

  • Direct access: 941 ns par object (reference time)
  • Naive introspection: 4613 ns per object ( + 390 % )
  • Introspection with field cached in a HashMap: 1376 ns par object ( + 46 % )
  • Introspection with field cached in a local variable: 1105 ns per object ( + 17 % )

I can post the code if required.

For me, if you keep all class and field look-ups out of the loops, the introspection costs are negligible.

Regards,

Orden
  • 589
  • 3
  • 13
0

Only the original lookup is extensive, once you have all the informations you need about a class and its methods, there's not much difference.

Since a business tier is supposed to be running a long time, startup is a little slower, but after that there's no performance hit.

krtek
  • 26,334
  • 5
  • 56
  • 84