java反射学习
1、
privateGetDeclareMethods 获取jvm或缓存中该class申明的方法列表,searchMethods将从返回的方法列表里找到一个匹配名称和参数的方法对象。
2、
如果找到了匹配的Method,copy一份Method返回
3.所次每次调用getDeclaredMethod
方法返回的Method
对象其实都是一个新的对象,且新对象的root
属性都指向原来的Method
对象,如果需要频繁调用,最好把Method
对象缓存起来
4.privateGetDecaleredMethods.中的reflectionData,reflectionData是SoftReference,
在内存紧张时可能会被回收,不过也可以通过
-XX:SoftRefLRUPolicyMSPerMB
参数控制回收的时机,只要发生GC就会将其回收,如果reflectionData
被回收之后,又执行了反射方法,那只能通过newReflectionData
方法重新创建一个这样的对象了,newReflectionData
方法
5.
在
privateGetDeclaredMethods
方法中,如果通过reflectionData()
获得的ReflectionData
对象不为空,则尝试从ReflectionData
对象中获取declaredMethods
属性,如果是第一次,或则被GC回收之后,重新初始化后的类属性为空,则需要重新到JVM中获取一次,并赋值给ReflectionData
,下次调用就可以使用缓存数据了。newReflectionData中使用Unsafe方法,通过unsafe.compareAndSwapObject
方法重新设置reflectionData
字段;
Method 调用,使用Invoke方法
1.
这里的MethodAccessor
对象是invoke
方法实现的关键,一开始methodAccessor
为空,需要调用acquireMethodAccessor
生成一个新的MethodAccessor
对象,MethodAccessor
本身就是一个接口
DelegatingMethodAccessorImpl
对象就是一个代理对象,负责调用被代理对象delegate
的invoke
方法,其中delegate
参数目前是NativeMethodAccessorImpl
对象,所以最终Method
的invoke
方法调用的是NativeMethodAccessorImpl
对象invoke
方法
这里用到了
ReflectionFactory
类中的inflationThreshold
,当delegate
调用了15次invoke
方法之后,如果继续调用就通过MethodAccessorGenerator
类的generateMethod
方法生成MethodAccessorImpl
对象,并设置为delegate
对象,这样下次执行Method.invoke
时,就调用新建的MethodAccessor
对象的invoke()
方法了。
15次的意思:实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。 Sun的JDK是从1.4系开始采用这种优化的。
generateMethod
方法在生成MethodAccessorImpl
对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass
创建对应的class对象
这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。