WeakCache解析
它是一个具有二级缓存的弱引用类,一级缓存的key是弱引用,二级缓存是强引用。其中key是根据入参直接传入的,二级缓存的key和value都是是根据一级缓存的key和value通过各自的工厂方法(subKeyFactory和valueFactory)计算得到的。当弱引用被clear后,entries会被以惰性(lazily)方式被删除。
在动态代理中,一级缓存是根据classloader生成的map,二级缓存是根据classloader和接口类数组生成的map。
我们这里主要分析下weakcache的get方法,动态代理中,创建代理类相关的两个类KeyFactory和ProxyClassFactory就是在这个地方被调用的。
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//删除失效缓存(依靠弱引用机制),WeakCache的所有方法调用都会先进行这一步处理
expungeStaleEntries();
//根据入参的key生成一级key,这里的入参其实就是接口类的classLoader
//CacheKey集成自WeakReference类,是一个弱引用类,refQueue是弱引用队列,
//如果CacheKey引用的对象被回收了,jvm会调用Cachekey父类中的enqueue方法,将自己放入refQueue中
//expungeStaleEntries方法会遍历refQueue,将refQueue中Cachekey对应的一级缓存和二级缓存全部移除,从而避免内存泄露
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 以懒加载方式生成二级缓存的valueMap(在动态代理中,这里面存放的就是代理类)
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
//因为weakcache本身是处于一个高并发调用的场景的,不同的线程可能同时在生成动态代理,为了保证性能,没有加锁
//采用CAS无锁机制,如果map中存在存在值,就用旧的值,如果不存在,才写入新的值
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// 根据subKeyFactory所定义的规则,生成对应的二级缓存key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 获取二级缓存对应的value,如果没有对应的value,则利用valueFactory创建
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 死循环,直到找到对应的代理类为止(没找到会生成一个然后返回)
while (true) {
if (supplier != null) {
// supplier 可能是Factory 或者 CacheValue(就是二级缓存对应的值,也是一个弱引用,且实现了Supplier接口)的实例
// supplier就是一个带有get方法的接口,Factory作为Weakcache的内部类实现了该接口
V value = supplier.get();
if (value != null) {
return value;
}
}
// 二级缓存里面supplier不存在
// 或者supplier存在,但get的值为null,(因为CacheValue是个弱引用,在动态代理中引用的是实际的代理类,如果改代理类不再使用被gc了,对应CacheValue的get值也就为null)
// 或者Factory没有成功的初始化CacheValue(即Factory调用get方法返回的Value为null)
// 重新构造一个Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
// 按照CAS无锁实现,将构造好的factory分配给supplier
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
// supplier不为空,走到这一步说明旧的supplier是无效的,所以用新生成的factory进行替换
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
到这里,get方法基本理顺了,但还有个问题,我们只看到subKeyFactory创建二级缓存key的调用,却没有看见valueFactory生成代理类是在哪里被调用的,不要急,我们看下Factory的get方法。在将Factory赋值给supplier后,是通过get方法获取到最终的代理类。Factory的get方法源码如下:
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// 出于线程安全的原因,supplier可能被改变了,所以在这里要再次做检测,判断当前二级缓存key对应的supplier是否发生改变,如果改变了则返回重新进入上级的循环
return null;
}
// else still us (supplier == this)
// 创建新的value值(代理类),这里就是valueFactory实际调用的地方,因为factory是weakcache的内部类,所以可以直接访问valueFactory,不需要再构造函数中传入参数
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// 利用代理类创建弱引用对象cacheValue,保证代理类不使用后,能够进行gc,不出现内存泄露
CacheValue<V> cacheValue = new CacheValue<>(value);
// 将cacheValue放入reverseMap中,记录当前的代理类的弱引用对象
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
至此,动态代理中weakcache的使用基本就讲完了,关于弱引用的部分可以看链接“基础-JDK动态代理-弱引用”,weakcache的功能有下面几个,
-
利用KeyFactory的apply方法,根据传入的key和parameters生成一个二级key(传入的key为一级key)
-
判断二级key对应的cachevalue是否存在,如果不存在,则调用valueFactory的apply方法创建cachevalue
-
使用弱引用做一级key,二级缓存中cachevalue也是弱引用,解决内存泄露问题