代理4 動(dòng)態(tài)代理的緩存機(jī)制

1.背景

上一節(jié)大致介紹了Proxy動(dòng)態(tài)代理的原理,從幾個(gè)疑問(wèn)上面分析,這一節(jié)介紹一下動(dòng)態(tài)代理的緩存機(jī)制。網(wǎng)上的資源比較少。
可以懷著下面幾個(gè)問(wèn)題閱讀源碼

為什么要緩存
緩存的內(nèi)容是什么
哪里調(diào)用的緩存
緩存的實(shí)現(xiàn)機(jī)制
緩存的過(guò)期機(jī)制

2.屬性說(shuō)明

Proxy涉及的緩存主要在java.lang.reflect.WeakCache這個(gè)類中
該類存在于jdk反射的包中
當(dāng)生成代理對(duì)象的時(shí)候會(huì)用到

java.lang.reflect.Proxy#newProxyInstance
java.lang.reflect.Proxy#getProxyClass0
java.lang.reflect.WeakCache#get

看這個(gè)類的結(jié)構(gòu)

//用了Reference記錄引用隊(duì)列,java gc時(shí)配合清除緩存用
private final ReferenceQueue<K> refQueue  = new ReferenceQueue<>();
//緩存的底層實(shí)現(xiàn)
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//記錄所有緩存中的CacheKey,配合緩存的過(guò)期機(jī)制
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//兩個(gè)二元操作函數(shù)
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;

這里最重要的的是map,將key-value介紹如下

字段 意義 備注
key 通過(guò)classLoader來(lái)進(jìn)行一級(jí)緩存 實(shí)際類型為CacheKey
value 二級(jí)緩存 后續(xù)會(huì)介紹

源碼中把這個(gè)value的變量稱為valuesMap,這里順便借用,介紹這個(gè)“二級(jí)緩存”是干嘛的

字段 意義 備注
key 二級(jí)緩存的key,由classLoader和interfaces[]標(biāo)識(shí)代理類 實(shí)際類型在java.lang.reflect.Proxy.KeyFactory#apply,為Key1或者Key2或者KeyX或者object,取決于代理類實(shí)現(xiàn)的接口數(shù)量
value 用于返回代理類(即Class<?>) 第一次存儲(chǔ)實(shí)際類型為java.lang.reflect.WeakCache.Factory#Factory,之后取出時(shí),都是java.lang.reflect.WeakCache.CacheValue

那么為什么要用緩存,緩存的內(nèi)容是什么
緩存就是在動(dòng)態(tài)代理生成代理類時(shí),只用生成一次后面盡量直接復(fù)用

3.實(shí)現(xiàn)機(jī)制

java.lang.reflect.WeakCache#get

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

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // 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)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

注意下面幾點(diǎn)

3.1 ConcurrentMap的get以及putIfAbsent的使用

可以看到有幾處代碼,形如

ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

這個(gè)是為了保證在多線程下,去獲取同一個(gè)key,拿到的是同一個(gè)引用對(duì)象
可以理解像單例模式里面要判斷兩次,來(lái)保證不會(huì)多次生成對(duì)象

if(instance==null) {
  synchronize(XXX.class) {
    if(instance==null){
      instance = new XXX();
    }
  }
}

可以參考http://wxl24life.iteye.com/blog/1746794

3.2 為什么說(shuō)第一次valuesMap里面value的實(shí)際類型和之后不一樣

根據(jù)Supplier<V> supplier = valuesMap.get(subKey);中supplier值以及在while循環(huán)第幾次,按照時(shí)間先后考慮

3.2.1 supplier==null,第一次while循環(huán)

這種出現(xiàn)在第一次請(qǐng)求中,二級(jí)緩存沒(méi)有該key

邏輯為

supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
    // successfully installed Factory
    supplier = factory;
}

此時(shí)valuesMap里面的key是subKey
subKey是

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

根據(jù)java.lang.reflect.Proxy.KeyFactory#apply
subKey實(shí)際類型是Key1,Key2,Object或者KeyX,根據(jù)實(shí)現(xiàn)的接口數(shù)量決定

private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }

3.2.2 supplier==null,第二次while循環(huán)(或者因其他原因,第n次while循環(huán))

這種出現(xiàn)在第一次請(qǐng)求中,二級(jí)緩存沒(méi)有該key,supplier被賦值之后
邏輯為

if (supplier != null) {
  // supplier might be a Factory or a CacheValue<V> instance
  V value = supplier.get();
  if (value != null) {
     return value;
  }
 }

supplier在第一次循環(huán)時(shí),已經(jīng)為factory了,這里調(diào)用get方法,就是進(jìn)入
java.lang.reflect.WeakCache.Factory#get的邏輯

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                //supplier和當(dāng)前supplier不等,驗(yàn)證不正確
                return null;
            }
            V value = null;
            try {
                //生成代理類對(duì)應(yīng)的Class<?>信息
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            assert value != null;
            CacheValue<V> cacheValue = new CacheValue<>(value);

            //把之前二級(jí)緩存的value替換為當(dāng)前的CacheValue
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }
            return value;
        }

其中, value = Objects.requireNonNull(valueFactory.apply(key, parameter));就是代理類

java.lang.reflect.Proxy.ProxyClassFactory#apply //返回Class<?>
//具體生成細(xì)節(jié)參考上一節(jié)

在生成value之后

//把當(dāng)前的值即this(類型為Factory)替換為了cacheValue(類型CacheValue)
valuesMap.replace(subKey, this, cacheValue)

并且這一次get()最終返回的是value;也就是Class<?>

3.2.3 supplier!=null

出現(xiàn)在之后的某一次請(qǐng)求中,二級(jí)緩存已經(jīng)有該key-value

if (supplier != null) {
  // supplier might be a Factory or a CacheValue<V> instance
  V value = supplier.get();
  if (value != null) {
     return value;
  }
 }

此時(shí),supplier的類型為CacheValue
在Factory#get中生成時(shí),就是
CacheValue<V> cacheValue = new CacheValue<>(value);
CacheValue#get方法,最終轉(zhuǎn)入java.lang.ref.Reference#get,返回的就是這個(gè)value,即構(gòu)造函數(shù)的參數(shù)value

3.3緩存的過(guò)期機(jī)制

這里用了java.lang.reflect.WeakCache.CacheKey是繼承WeakReference的
即弱引用,在java gc中,滿足適當(dāng)?shù)臈l件時(shí),當(dāng)一個(gè)referent是ROOT可達(dá)且為弱引用時(shí),會(huì)將其放入對(duì)應(yīng)的referenceQueue。
核心函數(shù)在

private void expungeStaleEntries() {
        CacheKey<K> cacheKey;
        while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
            cacheKey.expungeFrom(map, reverseMap);
        }
    }

可能會(huì)奇怪這里并沒(méi)有refQueue的enqueue操作,這個(gè)操作是java gc的時(shí)候做的,這里不深究。
只要知道gc時(shí),某些場(chǎng)景會(huì)進(jìn)行refQueue的enqueue即可。
那么,之后再調(diào)用expungeStaleEntries()就能清除過(guò)期的緩存。

4. 思考

4.1 為什么要二級(jí)緩存

一級(jí)緩存用來(lái)區(qū)分classLoader,二級(jí)緩存用來(lái)區(qū)分實(shí)現(xiàn)的接口
生成類的緩存是按照ClassLoader來(lái)劃分的,這是因?yàn)轭惖膮^(qū)分不僅根據(jù)類名還根據(jù)裝載類的ClassLoader,也就是說(shuō)同一個(gè)類被不同的ClassLoader加載,那么它們也是不同的.
參考
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3

4.2 ConcurrentHashMap想要獲取SingleValue的方法(嚴(yán)格來(lái)說(shuō)是同一個(gè)引用)

如果putIfAbsent返回值不為null,那么返回該值,不要重新put
http://wxl24life.iteye.com/blog/1746794

4.3 二級(jí)緩存valueMaps的value的類型不一樣

第一次是Factory,調(diào)用get()時(shí),生成代理類信息之后,將CacheValue作為值替換了當(dāng)前key對(duì)應(yīng)的value
之后調(diào)用,都是CacheValue.get()返回java.lang.ref.Reference#get,最終就是CacheValue的構(gòu)造函數(shù)參數(shù),即代理類信息Class<?>

4.4 一級(jí)緩存的key-value與二級(jí)緩存的key-value

4.5 在'背景'中提出的問(wèn)題,在上文中都有解釋

4.6 緩存的過(guò)期機(jī)制

利用java.lang.reflect.WeakCache.CacheKey繼承WeakReference,配合ReferenceQueue使用。
在java gc時(shí)某些條件下觸發(fā)。

5.暫時(shí)不明白的問(wèn)題

5.1 reverseMap的value有什么用?

感覺(jué)純粹是利用ConcurrentHashMap的"Concurrent"的特性,value并沒(méi)有什么用,代碼里面并沒(méi)有處理里面的value,都是Boolean.TRUE;

5.2 實(shí)現(xiàn)二級(jí)緩存的邏輯為什么要這么寫,不能一步到位嗎?

為什么先放入Factory,調(diào)用Factory.get時(shí)進(jìn)行replace操作再放入CacheValue,不能一步到位寫入CacheValue后面直接用嗎

6. refer

concurrentHashMap 的putIfAbsent完成single value
http://wxl24life.iteye.com/blog/1746794

cglib的緩存說(shuō)明
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3

gc 與 reference
http://www.cnblogs.com/maxmys/p/3998842.html
http://hongjiang.info/java-referencequeue/
http://zhang-xzhi-xjtu.iteye.com/blog/413159

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光劍書架上的書閱讀 3,948評(píng)論 2 8
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,778評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評(píng)論 19 139
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,839評(píng)論 0 11
  • 2017年4月19日 (下午11:10) 一大早7:30從家里出發(fā),10:40的火車離開北...
    紅梅_7a37閱讀 3,204評(píng)論 3 4