深入分析 Java 方法反射的实现原理
博主说:Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。在本文中,占小狼分析了 Java 反射机制的实现原理(源码),感兴趣的同学可以通过阅读本文花上几分钟了解了解。
正文
方法反射实例
public class ReflectCase {
public static void main(String[] args) throws Exception {
Proxy target = new Proxy();
Method method = Proxy.class.getDeclaredMethod("run");
method.invoke(target);
}
static class Proxy {
public void run() {
System.out.println("run");
}
}
}
通过 Java 的反射机制,可以在运行期间调用对象的任何方法,如果大量使用这种方式进行调用,会有性能或内存隐患么?为了彻底了解方法的反射机制,只能从底层代码入手啦!
Method 获取
调用 Class 类的getDeclaredMethod
可以获取指定方法名和参数的方法对象 Method。
getDeclaredMethod
其中privateGetDeclaredMethods
方法从缓存或 JVM 中获取该 Class 中申明的方法列表,searchMethods
方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。
searchMethods
如果找到一个匹配的 Method,则重新复制一份返回,即Method.copy()
方法。
所次每次调用getDeclaredMethod
方法返回的 Method 对象其实都是一个新的对象,且新对象的root
属性都指向原来的 Method 对象,如果需要频繁调用,最好把 Method 对象缓存起来。
privateGetDeclaredMethods
从缓存或 JVM 中获取该 Class 中申明的方法列表,实现如下:
其中reflectionData()
方法实现如下:
这里有个比较重要的数据结构ReflectionData
,用来缓存从 JVM 中读取类的如下属性数据:
从reflectionData()
方法实现可以看出:reflectionData
对象是SoftReference
类型的,说明在内存紧张时可能会被回收,不过也可以通过-XX:SoftRefLRUPolicyMSPerMB
参数控制回收的时机,只要发生GC
就会将其回收,如果reflectionData
被回收之后,又执行了反射方法,那只能通过newReflectionData
方法重新创建一个这样的对象了,newReflectionData
方法实现如下:
通过unsafe.compareAndSwapObject
方法重新设置reflectionData
字段;在privateGetDeclaredMethods
方法中,如果通过reflectionData()
获得的ReflectionData
对象不为空,则尝试从ReflectionData
对象中获取declaredMethods
属性,如果是第一次,或则被GC
回收之后,重新初始化后的类属性为空,则需要重新到 JVM 中获取一次,并赋值给ReflectionData
,下次调用就可以使用缓存数据了。
Method 调用
获取到指定的方法对象 Method 之后,就可以调用它的invoke
方法了,invoke
实现如下:
应该注意到:这里的MethodAccessor
对象是invoke
方法实现的关键,一开始methodAccessor
为空,需要调用acquireMethodAccessor
生成一个新的MethodAccessor
对象,MethodAccessor
本身就是一个接口,实现如下:
在acquireMethodAccessor
方法中,会通过ReflectionFactory
类的newMethodAccessor
创建一个实现了MethodAccessor
接口的对象,实现如下:
在ReflectionFactory
类中,有 2 个重要的字段:noInflation
(默认false
)和inflationThreshold
(默认15
),在checkInitted
方法中可以通过-Dsun.reflect.inflationThreshold=xxx
和-Dsun.reflect.noInflation=true
对这两个字段重新设置,而且只会设置一次;如果noInflation
为false
,方法newMethodAccessor
都会返回DelegatingMethodAccessorImpl
对象,DelegatingMethodAccessorImpl
的类实现:
其实,DelegatingMethodAccessorImpl
对象就是一个代理对象,负责调用被代理对象delegate
的invoke
方法,其中delegate
参数目前是NativeMethodAccessorImpl
对象,所以最终 Method 的invoke
方法调用的是NativeMethodAccessorImpl
对象invoke
方法,实现如下:
这里用到了ReflectionFactory
类中的inflationThreshold
,当delegate
调用了15
次invoke
方法之后,如果继续调用就通过MethodAccessorGenerator
类的generateMethod
方法生成MethodAccessorImpl
对象,并设置为delegate
对象,这样下次执行Method.invoke
时,就调用新建的MethodAccessor
对象的invoke()
方法了。这里需要注意的是:generateMethod
方法在生成MethodAccessorImpl
对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass
创建对应的 Class 对象,实现如下:
在ClassDefiner.defineClass
方法实现中,每被调用一次都会生成一个DelegatingClassLoader
类加载器对象:
这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行啦!
转载声明:本文转自简书「占小狼」,深入分析Java方法反射的实现原理。
上一篇: Spring源码之循环依赖