設計模式(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]