Java的反射机制,相信很多人都已经用过,我们在使用的时候,最大的一个问题除了使用复杂之外,是性能隐忧,本文主要针对性能隐忧进行阐述。
反射为什么慢?
在stackoverflow上,已经有人问了这个问题:http://stackoverflow.com/questions/1392351/java-reflection-why-is-it-so-slow
有一个回复很多人支持,如下
- The compiler can do no optimization whatsoever as it can have no real idea about what you are doing. This probably goes for the
JIT
as well - Everything being invoked/created has to be discovered (i.e. classes looked up by name, methods looked at for matches etc)
- Arguments need to be dressed up via boxing/unboxing, packing into arrays,
Exceptions
wrapped inInvocationTargetException
s and re-thrown etc. - All the processing that Jon Skeet mentions here.
简单意译:编译器不能对代码对优化,所有的反射操作都需要类似查表的操作,参数需要封装,解封装,异常也重新封装,rethrow等等。而四点具体的内容如下:
Compare that with everything that reflection has to do:
- Check that there's a parameterless constructor
- Check the accessibility of the parameterless constructor
- Check that the caller has access to use reflection at all
- Work out (at execution time) how much space needs to be allocated
- Call into the constructor code (because it won't know beforehand that the constructor is empty)
基本上都是check这check那,还有无法被优化掉的调用。
所以,这些额外的操作,就可以理解为什么反射比较慢了,但是,究竟哪些环节比较耗时呢?
反射究竟是什么地方慢?
我写了一段测试代码,可能有漏洞,欢迎大家指出!
- class A{
- public String field = "";
- public boolean doSomething(){
- return true;
- }
- }
- A object = new A();
- Class c = A.class;
- int loops = 10000000;
- A[] As = new A[loops];
- boolean boolRet = false;
- long start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- As[i] = new A();
- }
- System.out.println( loops + " regular instaniations:" + (System.currentTimeMillis() - start) + " milliseconds." );
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- As[i] = (A) c.newInstance();
- }
- System.out.println( loops + " reflection instaniations:" + (System.currentTimeMillis() - start) + " milliseconds." );
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- boolRet = object.doSomething();
- }
- System.out.println( loops + " regular method calls:" + (System.currentTimeMillis() - start) + " milliseconds." );
- java.lang.reflect.Method method = c.getMethod( "doSomething", null );
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- boolRet = (Boolean) method.invoke( object, null );
- }
- System.out.println( loops + " reflective method calls without lookup:" + (System.currentTimeMillis() - start) + " milliseconds." );
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- method = c.getMethod( "doSomething", null );
- method.invoke( object, null );
- }
- System.out.println( loops + " reflective method calls with lookup:" + (System.currentTimeMillis() - start) + " milliseconds." );
- String fieldValue = "";
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- fieldValue = object.field;
- }
- System.out.println( loops + " regular field get value:" + (System.currentTimeMillis() - start) + " milliseconds." );
- Field field = null;
- field = c.getField("field");
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- fieldValue = (String) field.get(object);
- }
- System.out.println( loops + " reflective field get value without lookup:" + (System.currentTimeMillis() - start) + " milliseconds." );
- start = System.currentTimeMillis();
- for( int i = 0; i < loops; i++ )
- {
- field = c.getField("field");
- fieldValue = (String) field.get(object);
- }
- System.out.println( loops + " reflective field get value with lookup:" + (System.currentTimeMillis() - start) + " milliseconds." );
执行后结果如下(JDK1.6):
10000000 regular instaniations:1557 milliseconds.
10000000 reflection instaniations:2893 milliseconds.
10000000 regular method calls:6 milliseconds.
10000000 reflective method calls without lookup:3460 milliseconds.
10000000 reflective method calls with lookup:10119 milliseconds.
10000000 regular field get value:5 milliseconds.
10000000 reflective field get value without lookup:3897 milliseconds.
10000000 reflective field get value with lookup:12560 milliseconds.
可见:
- 反射比较耗时,除了初始化对象之外,其他操作性能相差不是一个数量级
- 使用反射调用方法和获取成员值,查找比真正执行的操作更耗时
反射的优化手段有什么?
基于以上测试的结果,我们可以有以下优化手段
- 不要重复反射
- 将最慢的反射结果缓存起来,如方法和成员的获取,从而较少“查找”地使用反射
- 使用代码动态生成技术,通过调用代理类的方式来模拟反射。如ReflectASM开源项目。
转载于:https://blog.51cto.com/hahaeason/1184463