JDK动态代理中的WeakCache解析

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也是弱引用,解决内存泄露问题

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值