0%

反射效率低的原因

文章字数:711,阅读全文大约需要2分钟

整理自原文

反射效率低的原因

  1. Method#invoke方法会对参数做封装和解封操作
    invoke方法参数是Object[]类型,如果方法的参数是简单类型,那么需要先包装成Object类型。例如long需要装换成Long。此时产生了多余的Long类型的Objec。生成动态字节码,并加载到jvm的方法MethodAccessorGenerator#emitInvoke会将参数恢复到之前的类型,同时做参数校验。
    反射调用的时候可能会将参数进行多余的封装和解封,产生不必要的内存浪费。调用次数过多甚至会导致GC(感觉有点夸张)

  2. 需要检测方法可见性
    反射每次调用都必须检查方法的可见性(Method.invoke)

  3. 需要校验参数
    反射必须检查每个实际参数和形式参数的类型匹配(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里)

  4. 方法难以内联
    Method.invoke()自身难以被内联到方法调用。

  5. JIT无法优化
    反射涉及到动态加载的类型,无法优化。

解决

文章没给,但是自己能想到几个

  1. 方法参数使用包装类型(减少了包装和拆包的步骤)
  2. 将反射获取的Method等对象缓存下来,这样在下次调用的时候可以减少生成的开销。

总结

  1. 获取方式的方法
  • getMethod / getDeclaredMethod
    调用反射时现需要创建Class对象(Class.forName),然后获取Method对象(getMethod / getDeclaredMethod),最后才是invoke调用。
    相同:getMethodgetDeclaredMethod的内部结构都是检查方法权限获取方法返回方法的拷贝
    不同:getMethod检查和获取的方法可以是自身的也可以是父类的,getDeclaredMethod获取的方法只能是自身的。

  • checkMemberAccess
    第一步,检查方法权限。getMethod的传入值之一是Member.PUBLICgetDeclaredMethodMember.DECLARED。一个检查父类和自身,另一个只有自身方法。

  • getMethod0
    getMethod方法检查完方法权限后调用getMethod0获取方法

  • getMechodsRecursive
    递归查找父类方法

  • privateGetDeclaredMethods
    获取自身方法

  • Method#copy

  1. 调用方式的方法
  • checkAccess
  • acquireMethodAccessor
  • MethodAccessor#invoke
  1. 方式效率低的原因
  • Method#invoke 方法会对参数做封装和解封操作
  • 需要检查方法的可见性
  • 需要校验参数
  • 反射方法难以内联
  • JIT无法优化