java 反射真的慢吗?
大家似乎有个默认的常识:java的反射性能不好,应该使用asm、cglib之类的库替代。
看java的反射实现会有两种情况,参考NativeMethodAccessorImpl
1.调用native方法,涉及到方法查找,
2.生成字节码,动态加载类,使用MethodAccessorGenerator
就是说在调用次数超过阈值15之后,就会动态生成类,来执行反射方法,本质上和直接调用时一样的。动态生成类的模式和cglib也是一样的。
跑了几个测试看看,计算的平均每次用时,用的mac jdk8,jvm参数:-server -Xmx4g -Xms4g -Xmn512m -Xss256k -verbose:gc (原来想贴一下代码的,简书的“引用”实在太难用了)
循环次数 1000 10*1000 100*1000 1000*1000
cglib 1392ns 984ns 228ns 78ns
反射 4753ns 980ns 163ns 36ns
从测试结果看出来,次数越多,反射的性能越好,这是为什么呢?和jit编译有关,使用-XX:+PrintCompilation可以看到,循环次数到达1000*1000的时候,不仅编译层数达到了4,而且发生了OSR编译(栈上替换),运行在OSR代码中的次数越多,性能越好。
同样的cglib的FastClass也会发生jit编译,也会使用OSR编译,但是性能比不过反射,同时通过监控内存,发现cglib使用的内存也比较多,主要是动态生成类的代码不一样。
cglib的FastClass是针对一个类动态生成一个新的类;反射是针对每一个方法生成一个新的类,方法体比较小,更适合jit做动态编译、内联。
加上-XX:+PrintInlining 参数,看内联的情况,反射的方法都经过了内联优化,而使用cglib生成的代码无法内联,容易出现hot method too big的情况。
查看cglib代码可以通过System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"./");将动态生成的代码保存到文件。