《java集合》--WeakHashMap

《java集合》–WeakHashMap

說明:此文章基於jdk1.8

參考:Java WeakHashMap 源碼解析

簡介

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方法

image

image

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;
      }
    }
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章