《java集合》–WeakHashMap
說明:此文章基於jdk1.8
簡介
WeakHashMap同HashMap的數據結構一樣,它的主要目的是爲了優化jvm的垃圾回收,通過弱引用讓垃圾回收器更加智能的回收無用的對象。
弱引用(WeakReference)。我們都知道Java中內存是通過GC自動管理的,GC會在程序運行過程中自動判斷哪些對象是可以被回收的,並在合適的時機進行內存釋放。GC判斷某個對象是否可被回收的依據是,是否有有效的引用指向該對象。如果沒有有效引用指向該對象(基本意味着不存在訪問該對象的方式),那麼該對象就是可回收的。這裏的“有效引用”並不包括弱引用。也就是說,雖然弱引用可以用來訪問對象,但進行垃圾回收時弱引用並不會被考慮在內,僅有弱引用指向的對象仍然會被GC回收。
當弱引用指向的對象只能通過弱引用(沒有強引用或弱引用)訪問時,GC會清理掉該對象,之後,引用對象會被放到
ReferenceQueue
中。也就是說如果WeakHashMap中的key值沒有其它引用引用key的話,那麼gc將會回收key對應的對象,然後在對WeakHashMap進行增刪改查時遍歷隊列中的key對象,移除map中key對應的entry對象,gc回收WeekHashMap 的這個特點特別適用於需要緩存的場景,在緩存場景下,由於內存是有限的,不能緩存所有對象;對象緩存命中可以提高系統效率,但緩存MISS也不會造成錯誤,因爲可以通過計算重新得到。
數據結構
跟hashMap的存儲結構一樣
基本屬性
- private final ReferenceQueue queue = new ReferenceQueue<>(); 當gc回收key對象時,會將key對象放入quene中,然後在對WeakHashMap進行增刪改查時遍歷隊列中的key對象,移除map中key對應的entry對象,gc回收
- 其它屬性同HashMap一樣
構造器
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
}
private Entry<K,V>[] newTable(int n) {
return (Entry<K,V>[]) new Entry<?,?>[n];
}
存儲的Entry
WeakHashMap中的key存儲的時候使用的是弱引用,當沒有其它指針引用該key對象的時候,gc會回收key對象,之後引用的key對象會被放到
ReferenceQueue
中。
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
//這裏把key傳給了父類WeakReference,說明key爲弱引用(沒有顯式的 this.key = key),所有如果key只有通過弱引用訪問時,key會被 GC 清理掉,同時該key所代表的Entry會進入queue中,等待被處理
super(key, queue);
//value爲強引用(有顯式的 this.value = value ),但這並不影響
this.value = value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings("unchecked")
public K getKey() {
//get():return this.referent;
////在獲取key時需要unmaskNull,因爲對於null的key,是用WeakHashMap的內部成員屬性來表示的
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public int hashCode() {
K k = getKey();
V v = getValue();
return Objects.hashCode(k) ^ Objects.hashCode(v);
}
public String toString() {
return getKey() + "=" + getValue();
}
}
static Object unmaskNull(Object key) {
return (key == NULL_KEY) ? null : key;
}
在對
WeakHashMap
進行增刪改查時,都調用了expungeStaleEntries
方法
private void expungeStaleEntries() {
//獲取已被回收key對象的隊列循環gc回收對應的entry對象
for (Object x; (x = queue.poll()) != null; ) {
//加同步鎖
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
//根據key值獲取entry所在table的下標
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
// while 循環遍歷衝突鏈
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
//將entry的value置爲空,讓gc回收
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}