前驅知識點
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方法時,纔會真正的返回對象,一般用來實現懶加載。