java面试题:如何提高反射效率?

怎样提高反射的效率?

一. 善用API

比如,尽量不要getMethods后再遍历筛选,而直接用getMethodmethodName来根据方法名获取方法

二、缓存大法好~

比如,需要多次动态创建一个类的实例的时候,有缓存的写法会比没有缓存要快很多:

// 1. 没有缓存
void createInstanceString className{ return Class.forNameclassName.newInstance;
} // 2. 缓存forName的结果
void createInstanceString className{ cachedClass = cache.getclassName; if cachedClass == null{ cachedClass = Class.forNameclassName; cache.setclassName, cachedClass; } return cachedClass.newInstance;
}

为什么?当然是因为forName太耗时了。

p.s. cache请自行实现

  1. 在系统启动阶段使用反射。
  2. 将反射得到元数据保存起来,使用时,只需从内存中调用即可。
  3. hotspot虚拟机会对执行次数较多的方法进行优化(例如使用jit技术)。
  4. 使用高性能的反射库,应该会比自己写缓存效果好。
javapublic Method getMethodString name, Class<?>... parameterTypes throws NoSuchMethodException, SecurityException { checkMemberAccessMember.PUBLIC, Reflection.getCallerClass, true; Method method = getMethod0name, parameterTypes; if method == null { throw new NoSuchMethodExceptiongetName + "." + name + argumentTypesToStringparameterTypes; } return method; } private Method getMethod0String name, Class<?>[] parameterTypes { if res = searchMethodsprivateGetDeclaredMethodstrue, name, parameterTypes != null { return res; } if !isInterface { Class<? super T> c = getSuperclass; if c != null { if res = c.getMethod0name, parameterTypes != null { return res; } } } Class<?>[] interfaces = getInterfaces; for int i = 0; i < interfaces.length; i++ { Class<?> c = interfaces[i]; if res = c.getMethod0name, parameterTypes != null { return res; } } return null; } private static Method searchMethodsMethod[] methods, String name, Class<?>[] parameterTypes { Method res = null; String internedName = name.intern; for int i = 0; i < methods.length; i++ { Method m = methods[i]; if m.getName == internedName && arrayContentsEqparameterTypes, m.getParameterTypes && res == null || res.getReturnType.isAssignableFromm.getReturnType res = m; } return res == null ? res : getReflectionFactory.copyMethodres; } 

顺便看一下getMethod方法,如果类的方法不是很多,getMethod和getMethods方法差距不是很大。差距在于自己写轮寻和系统自带轮寻之间的效率。

除了缓存还有没其他方法?

要使用类信息,少查找class, field, method,缓存很重要,还有办法是生成代理类。要复制对象可以用CGLib的BeanCopier 其原理是运行时动态生成了用于复制某个类的代码字节码。
要提高Method.invoke性能,可用Java 7的MethodHandle 由于Method.invoke的JIT优化,差距不大。
使用高版本JDK也很重要,反射性能一直在提高。

ReflectASM 通过字节码生成的方式加快反射速度

说了半天,都没有人提到”setAccessibletrue”…楼上的各位不是资深java程序员吧?

edit:
实话地告诉你,JDK1.6之后,对于method/field/constructor的invoke这类的反射,除了”setAccessibletrue”之外,再无须其它任何优化, 完爆以前cglib的fastmethod之流

当然了,将得到的method/field/constructor对象做缓存这是基本的

07-31 edit:
忘了补充一句:
如果你在jdk6上跑,且如果你反射的目标方法是getter/setter methods的话,记得加上配置:-XX:-UseFastEmptyMethods -XX:-UseFastAccessorMethods , 这两个配置的关闭是为了让accessor methods能够被jit; jdk7以上不需要设置这两个配置

使用反射框架,如joor,或者apache的commons相关工具类

发表评论

电子邮件地址不会被公开。 必填项已用*标注