a, 为了提高反射的性能,缓存显然是必须的。
class类内部提供了一个ReflectionData内部类用来存放反射数据的缓存,并声明了一个reflectionData域
,由于稍后进行按需延迟加载并缓存,所以这个域并没有指向一个实例化的ReflectionData对象。
在Class类的代码中可以看出来:
private static void
checkInitted() {
if
(
initted
)
return
;
AccessController.
doPrivileged
(
new
PrivilegedAction<Void>() {
public
Void run() {
if
(System.
out
==
null
) {
// java.lang.System not yet fully initialized
return null
;
}
// Doesn't use Boolean.getBoolean to avoid class init.
String val =
System.
getProperty
(
"sun.reflect.noCaches"
);
if
(val !=
null
&& val.equals(
"true"
)) {
useCaches
=
false
;
}
initted
=
true
;
return null
;
}
});
}
lass类内部有一个useCaches静态变量来标记是否使用缓存,这个值可以通过外部配置项sun.reflect.noCaches进行开关。
b. 对于这个
ReflectionData
的用法可以看Class类中获取类的构造函数这个方法来看
//
publicOnly指示是否只获取public的构造函数,我们常用的getConstructors方法是只返回public的构造函数,而 getDeclaredConstructors返回的是所有构造函数,由于java的构造函数不会继承,所以这里不包含父类的构造函数。
private
Constructor<
T
>[] privateGetDeclaredConstructors(
boolean
publicOnly) {
checkInitted
();
Constructor<
T
>[] res;
//这里缓存中读取reflectionData,如果还没有缓存,则创建一个reflectionData并设置到缓存。但是注意这个ReflectionData
//可能只是个空对象,里面并没有任何数据。
ReflectionData<
T
> rd = reflectionData(); ->
该方法比较重要在下文中c中有详细解释
if
(rd !=
null
) {
res = publicOnly ? rd.
publicConstructors
: rd.
declaredConstructors
;
if
(res !=
null
)
return
res;
//检查缓存中是否有数据 有的话直接返回
}
// No cached value available; request value from VM
if
(isInterface()) {
@SuppressWarnings
(
"unchecked"
)
Constructor<
T
>[] temporaryRes = (Constructor<
T
>[])
new
Constructor<?>[
0
];
res = temporaryRes; //接口没有构造函数
}
else
{
res = getDeclaredConstructors0(publicOnly);
//native方法,从jvm中获取
}
//如果代码执行到了这里,说明需要更新缓存了,将之前从jvm中请求到的数据放置到缓存中。
if
(rd !=
null
) {
if
(publicOnly) {
rd.
publicConstructors
= res;
}
else
{
rd.
declaredConstructors
= res;
}
}
return
res;
}
c. 比较重要的是reflectionData()这个调用。这个方法主要是用于延迟创建并缓存ReflectionData对象,注意是对象,里面并没有保存反射数据,这些数据只有在第一次执行相应的反射操作后才会被填充。
// Lazily create and cache ReflectionData
private
ReflectionData<
T
> reflectionData() {
SoftReference
<ReflectionData<
T
>> reflectionData =
this
.
reflectionData
;
int
classRedefinedCount =
this
.
classRedefinedCount
;
ReflectionData<
T
> rd;
//检查缓存是否有效,如果有效,从缓存中直接返回reflectionData。
if
(
useCaches
&&
reflectionData !=
null
&&
(rd = reflectionData.get()) !=
null
&&
rd.
redefinedCount
== classRedefinedCount) {
return
rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
/ / 无法使用到缓存,创建新的reflectionData
return
newReflectionData(reflectionData, classRedefinedCount);
}
对于这个SoftReference有必要解释一下,这个被称作为软引用: -> 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
example:
SoftReference ref =
new
SoftReference(
new
MyDate());
-> MyDate date = new MyDate();
// 由JVM决定运行
If(JVM.内存不足()) {
date = null;
System.gc();
}
d: 继续追查newReflectionData()这个方法
private
ReflectionData<
T
> newReflectionData(SoftReference<ReflectionData<
T
>> oldReflectionData,
int
classRedefinedCount) {
if
(!
useCaches
)
return null
;
// 这里使用cas模式更新reflectionData,主要是考虑多线程并发更新的问题,可能有另外一个线程已经更新了reflectionData,并且设置了有效的缓存数据,如果这里再次更新就把缓存数据覆盖了。
while
(
true
) {
ReflectionData<
T
> rd =
new
ReflectionData<>(classRedefinedCount);
// try to CAS it... cas是
compare and swap
比较并且替换的意思,
java.util.concurrent包中借助CAS 实现了区别于synchronouse同步锁的一种乐观锁。
if
(Atomic.
casReflectionData
(
this
, oldReflectionData,
new
SoftReference<>(rd))) {
return
rd;
// cas成功,那么我们swap的这个新对象是有效的。
}
// else retry
重新读取oldReflectionData,为下次重试cas做准备。
oldReflectionData =
this
.
reflectionData
;
classRedefinedCount =
this
.
classRedefinedCount
;
//先判断这个oldReflectionData是否有效。如果是无效的数据,需要去重试cas一个新的ReflectionData了。
if
(oldReflectionData !=
null
&&
(rd = oldReflectionData.get()) !=
null
&&
rd.
redefinedCount
== classRedefinedCount) {
return
rd;
}
}
}
另外cas就不追究了在多线程的那个地方再深入研究.
上面的几个代码片段是反射获取一个类的构造函数的主要方法调用。主要流程就是先从class内部的reflectionData缓存中读取数据,如果没有缓存数据,那么就从jvm中去请求数据,然后设置到缓存中,供下次使用。