Java Reflection Performance

前端 未结 14 1601
感动是毒
感动是毒 2020-11-22 08:25

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

相关标签:
14条回答
  • 2020-11-22 08:54

    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.

    0 讨论(0)
  • 2020-11-22 08:54

    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
    
    0 讨论(0)
  • 2020-11-22 08:55

    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.

    0 讨论(0)
  • 2020-11-22 08:58

    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.

    0 讨论(0)
  • 2020-11-22 09:02

    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.)

    0 讨论(0)
  • 2020-11-22 09:02

    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

    0 讨论(0)
提交回复
热议问题