文章字数:711,阅读全文大约需要2分钟
整理自原文
反射效率低的原因
Method#invoke
方法会对参数做封装和解封操作invoke
方法参数是Object[]
类型,如果方法的参数是简单类型,那么需要先包装成Object
类型。例如long
需要装换成Long
。此时产生了多余的Long
类型的Objec
。生成动态字节码,并加载到jvm
的方法MethodAccessorGenerator#emitInvoke
会将参数恢复到之前的类型,同时做参数校验。
反射调用的时候可能会将参数进行多余的封装和解封,产生不必要的内存浪费。调用次数过多甚至会导致GC
(感觉有点夸张)需要检测方法可见性
反射每次调用都必须检查方法的可见性(Method.invoke)需要校验参数
反射必须检查每个实际参数和形式参数的类型匹配(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里)方法难以内联
Method.invoke()
自身难以被内联到方法调用。JIT无法优化
反射涉及到动态加载的类型,无法优化。
解决
文章没给,但是自己能想到几个
- 方法参数使用包装类型(减少了包装和拆包的步骤)
- 将反射获取的
Method
等对象缓存下来,这样在下次调用的时候可以减少生成的开销。
总结
- 获取方式的方法
getMethod / getDeclaredMethod
调用反射时现需要创建Class
对象(Class.forName),然后获取Method
对象(getMethod / getDeclaredMethod),最后才是invoke
调用。
相同:getMethod
和getDeclaredMethod
的内部结构都是检查方法权限、获取方法、返回方法的拷贝。
不同:getMethod
检查和获取的方法可以是自身的也可以是父类的,getDeclaredMethod
获取的方法只能是自身的。checkMemberAccess
第一步,检查方法权限。getMethod
的传入值之一是Member.PUBLIC
,getDeclaredMethod
是Member.DECLARED
。一个检查父类和自身,另一个只有自身方法。getMethod0
getMethod
方法检查完方法权限后调用getMethod0
获取方法getMechodsRecursive
递归查找父类方法privateGetDeclaredMethods
获取自身方法Method#copy
- 调用方式的方法
- checkAccess
- acquireMethodAccessor
- MethodAccessor#invoke
- 方式效率低的原因
- Method#invoke 方法会对参数做封装和解封操作
- 需要检查方法的可见性
- 需要校验参数
- 反射方法难以内联
- JIT无法优化