设计模式(1-3)-动态代理(WeakCache的运用)

阅读本篇文章前,请事先阅读 理解Java的强引用、软引用、弱引用和虚引用。 看看什么是强引用、什么是弱引用及它们的用途,很必要!!!

上一节讲到,获取对应的代理类时,首先会从缓存中去拿,若拿不到才会去生成。实现缓存的储存,如何根据指定值拿到缓存都是由WeakCache这个类实现的。

我们先去探究一下WeakCache~

一、WeakCache

WeakCache有两级缓存,它的键值对: (key, sub-key) -> value。 一级缓存的keys和values是弱引用, 二级缓存的sub-keys是强引用

sub-keys, 根据keys和parameters使用subKeyFactory(构造器传入)计算出的。 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
values, 跟获取sub-keys类似,但是它是使用valueFactory(构造器传入)。 value = Objects.requireNonNull(valueFactory.apply(key, parameter));

为啥要使用WeakCache作为动态代理的缓存,我在网上看到了一个文章,What is the use case of WeakCache in Java? [closed], 可以将里面的图片对象 类比 生成的代理类(都要占用较大的内存),也不知正确与否(JVM早日把你干掉!!!)
我认为的原因是,

  1. 生成的代理类占用内存较大,key(弱引用, GC时会被回收)失效时, 可以被及时处理(expungeStaleEntries()就是处理key失效时,清楚掉对应的value的方法,在getcontainsValue,size被调用时调用)

简而言之,为了能用到时随时能用到,但是不影响GC,毕竟内存很宝贵的

1. 变量与构造器


    // 弱引用被回收时,将被添加到这个引用队列中
    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();

    // the key type is Object for supporting null key
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();

    // 保存value,当获取缓存的size时,就比较方便得到
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();

    // 生成subKey及value的类
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;

构造器:
java.lang.reflect.WeakCache#WeakCache

    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

2. 重要的方法

2.1 get()!!!

结合java.lang.reflect.Proxy#getProxyClass0使用到的WeakCache.get方法,我们看看其get()的原理

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {

        ...
        // 从这开始
        return proxyClassCache.get(loader, interfaces);
    }

java.lang.reflect.Proxy#proxyClassCache


    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

java.lang.reflect.Proxy.KeyFactory,根据实现接口个数返回不同的key, 有兴趣的同学可以去看看。
java.lang.reflect.Proxy.ProxyClassFactory, 上节讲过的,若指定的参数的缓存失效,就会使用该工厂类,生成对应的代理类。

tips: Supplier, 是一个Java8提供的一个函数式接口,结果的提供者,其get方法会返回一个结果。

有了以上的知识,我们一起看看WeakCacheget实现吧

java.lang.reflect.WeakCache#get: 👇

 public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        // 清理掉被GC的缓存
        expungeStaleEntries();
        
        // 将key包装成弱引用
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // 通过cachekey获取二级缓存 sub-keys - values
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        
        // 验证二级缓存valuesMap是否为null, 为null就初始化; 
        // 还有情况可能就是在初始化过程中,其他线程已经将它初始化,若这样,将实例指向第一次初始化的实例
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // subKeyFactory = KeyFactory, 此时就是根据实现的接口个数返回不同的对象
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

        // 第一次,valuesMap才被初始化,所以supplier为null
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        // 下面是一个轮询,直到拿到不为null的supplier且supplier.get()不为null为止
        while (true) {
            if (supplier != null) {
                // 第一次进来,supplier应该是Factory;
                // 第二次,获取的对应的参数未变的话,从valuesMap中获取到的supplier就是一个CacheValue<V>的实例了,此时就是从缓存中获取的了
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // 执行下面代码的原因:
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)
            
            // 懒加载
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                // 
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 一路成功的话,这里就会运行完毕,进行下一个循环,获取到值就直接返回value了
                    supplier = factory;
                }
                // supplier赋值的过程中,被其他线程提前赋值了, 继续循环
            } else {
                // 替换以前的supplier
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // 替换失败就使用目前的supplier,重试
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

我们去看看实现SuppilerFactory是如何提供返回结果的
java.lang.reflect.WeakCache.Factory

private final class Factory implements Supplier<V> {

        private final K key;
        private final P parameter;
        private final Object subKey;
        private final ConcurrentMap<Object, Supplier<V>> valuesMap;

        Factory(K key, P parameter, Object subKey,
                ConcurrentMap<Object, Supplier<V>> valuesMap) {
            this.key = key;
            this.parameter = parameter;
            this.subKey = subKey;
            this.valuesMap = valuesMap;
        }

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // 在我们等待时发生了改变:
                // 1. 被一个CacheValue替换了
                // 2. were removed because of failure
                // 此时,就返回null, 让WeakCache.get 继续循环
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                
                // !! 此时的valueFactory就是ProxyClassFactory; 这里会去生成代理类
                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;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // 将cacheValue保存起来
            reverseMap.put(cacheValue, Boolean.TRUE);

            // 用cacheValue替换原有的值 
            // 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;
        }
    }

对于java.lang.reflect.WeakCache#get,java.lang.reflect.WeakCache.Factory#get源码的一个总结:

  1. valuesMap找不到subKey对应的supplier, 此时supplier是Factory的实例,调用supplier.get()的时候就去调用java.lang.reflect.WeakCache.Factory#get
  2. valuesMapsubKey对应的supplier,此时supplier就是CacheValue的实例
    我们来看看supplier是CacheValue是,调用supplier.get(),实际调用的哪

java.lang.reflect.WeakCache#get
java.lang.reflect.WeakCache#get

按f7,step into ->
java.lang.ref.Reference#get
java.lang.ref.Reference#get

我们再来看看,CacheValue的结构

    private static final class CacheValue<V>
        extends WeakReference<V> implements Value<V>
    {
        private final int hash;

        CacheValue(V value) {
            // 点super进去看看,你就会知道了!!!
            super(value);
            this.hash = System.identityHashCode(value); // compare by identity
        }
        
        ... 
    }

java.lang.reflect.WeakCache.Value

    private interface Value<V> extends Supplier<V> {}

我们没有看到CacheValue中实现Supplierget(), 但是CacheValue的父类的父类Reference早已提供了get()这个方法,返回代理类的对象。

难道SupplierReference是天作之合? 希望我慢慢欣赏代码的美~

二、总结

结合了java.lang.reflect.Proxy#getProxyClass0中使用到WeakCache的地方,讲了讲,缓存获取(get)的整个过程。

三、参考

  1. 理解Java的强引用、软引用、弱引用和虚引用
  2. What is the use case of WeakCache in Java? [closed]

posted on 2021-11-02 14:29  ukyu  阅读(302)  评论(0编辑  收藏  举报

导航