reflect包-WeakCache類

前驅知識點

java 引用的四種基本類型

java 中包括四種基本類型,分別是FinalReference(強),SoftReference(軟),WeakReference(弱)、PhantomReference(虛),按照順序引用能力依次遞減。

FinalReference

  • 產生:使用new 關鍵字或者顯示使用FinalReference 構建的對象應用
  • 特點:強引用可以直接訪問目標對象;強引用所指向的對象在任何時候都不會被系統回收。JVM寧願拋出OOM異常,也不會回收強引用所指向的對象;強引用可能導致內存泄漏;
  • 使用

SoftReference

  • 產生:
Person p = new Person();
SoftReference sf = new SoftReference(p)
p = null;
System.gc();
System.out.println("值=" + sf.get())
  • 特點:軟引用是除了強引用外,最強的引用類型。可以通過java.lang.ref.SoftReference使用軟引用。一個持有軟引用的對象,不會被JVM很快回收,JVM會根據當前堆的使用情況來判斷何時回收。當堆使用率臨近閾值時,纔會去回收軟引用的對象。因此,軟引用可以用於實現對內存敏感的高速緩存。
    SoftReference的特點是它的一個實例保存對一個Java對象的軟引用, 該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference保存了對一個Java對象的軟引用後,在垃圾線程對 這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。一旦垃圾線程回收該Java對象之後,get()方法將返回null
知識點:
System.gc() 會通知虛擬機進行full gc,full gc 會損耗較大的性能。

WeakReference

  • 產生
Person p = new Person();
WeakReference wf = new WeakReference(p);
p = null;
System.out.println("full gc前="+wf.get())
System.gc();
Thread.sleep(1000);
System.out.println("full gc後=" + wf.get())
  • 特點: 當發生full gc 時會回收弱引用,在這之前將會可訪問。

PhantomReference(phantom: 幻影/幻象)

  • 使用:
public class TestReference {
    public final static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference sf = new PhantomReference(o, referenceQueue);
        o = null;
        System.out.println("before full gc=" + sf.get());
        System.out.println("has put to queue=" + referenceQueue.poll());
        System.gc();
        // 等待虛擬機調度gc完成
        Thread.sleep(1000);
        System.out.println("has put to queue="+referenceQueue.poll());
        System.out.println("after full gc=" + sf.get());
    }
}

輸出如下:

before full gc=null
has put to queue=null
has put to queue=java.lang.ref.PhantomReference@610455d6
after full gc=null
  • 特點:一個持有虛引用的對象,和沒有引用幾乎是一樣的,隨時可能被垃圾回收器回收。當試圖通過虛引用的get()方法取得強引用時,總是會失敗。並且,虛引用必須和引用隊列一起使用,它的作用在於跟蹤垃圾回收過程。
    當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在垃圾回收後,銷燬這個對象,將這個虛引用加入引用隊列。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動

WeakCache 類基本介紹

初次接觸WeakCache類是在理解Java proxy的源碼時看到的,要想理解Proxy的原理,WeakCache是過不去的坎,因爲Proxy獲取代理類的Class 實例通過代碼:

// 如果代理類在給定的Loader和接口下已經存在,那麼直接放回它的副本,
// 否則它將使用ProxyClassFactory 創建
return proxyClassCache.get(loader, interfaces);

而 proxyClassCache 正是WeakCache的實例

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

接下來看看WeakChache是什麼

WeakCache顧名思義,就是一個保存WeakReference值的緩存類,它接收三個泛型值:key, p代表參數類型,V 代表緩存值類型,它保存值的形式有點類似Map 的key,value,只不過它是一個變體:(key,(subKey->value)) 這樣的形式,key,value都是weakReference,sub-key強引用(發現在Proxy類中sub-key也是weak),但實際情況subKey的類型是否subKeyFactory決定的(subkey由subKeyFactory),value 是由valueFactory決定的,而key則一定weak類型.

它只有一個構造函數用來接收subKeyFactory函數以及valueFactory函數.

final class WeakCache<K, P, V> {
    // 隊列,用來記錄gc回收後的對象key
    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // key 是Ojbect類型,支持null,次結構代表(key,(subkey,value)
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;

    /**
     * Construct an instance of {@code WeakCache}
     *
     * @param subKeyFactory a function mapping a pair of
     *                      {@code (key, parameter) -> sub-key}
     * @param valueFactory  a function mapping a pair of
     *                      {@code (key, parameter) -> value}
     * @throws NullPointerException if {@code subKeyFactory} or
     *                              {@code valueFactory} is null.
     */
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
}    

再來看看主要的 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);
                }
            }
        }
    }

這裏主要步驟如下:

  • 驗證P 類型的值不能爲null
  • 擦除無效的緩存值(weakReference發生fullgc會回收掉)
  • 根據key生成一個WeakReference類型的key,有key的hash值決定
  • 根據生成的key 獲取 map 中的值,它的類型是ConcurrentMap<Object, Supplier>,它的形式是(subkey, value)
  • 進行非空判斷,空的話初始化key對應的值
  • 根據key,parameter兩個參數調用subKeyFactory函數來生成subkey
  • 根據subkey來獲取對應的value值,
  • while(true) 死循環直到能獲取值方法return 返回,這個過程會判斷 supplier 是否爲空,不爲空則調用get()方法返回值,其實V 是Factory類型,有這麼一行代碼:
  // 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
            }

Factory 實現了Supplier接口,並且重寫了get方法,在get方法中它會調用valueFactory.apply 方法返回真正的value值:

value = Objects.requireNonNull(valueFactory.apply(key, parameter));

Supplier

Java8 提供一個函數式接口,意思是作爲一個提供者,它包裝某個對象值,在調用Supplier的get方法時,纔會真正的返回對象,一般用來實現懶加載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章