最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java十三篇:WeakHashMap

2023-03-03 00:03 作者:小劉Java之路  | 我要投稿

集合的最后一篇:WeakHashMap

WeakHashMap 繼承于AbstractMap,實現(xiàn)了Map接口。和HashMap一樣,WeakHashMap 也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以是null。不過WeakHashMap的鍵是“弱鍵”。

在 WeakHashMap 中,當某個鍵不再正常使用時,會被從WeakHashMap中被自動移除。更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。某個鍵被終止時,它對應的鍵值對也就從映射中有效地移除了。這個“弱鍵”的原理呢?大致上就是,通過WeakReference和ReferenceQueue實現(xiàn)的。

WeakHashMap的key是“弱鍵”,即是WeakReference類型的;ReferenceQueue是一個隊列,它會保存被GC回收的“弱鍵”。實現(xiàn)步驟是:

(01) 新建WeakHashMap,將“鍵值對”添加到WeakHashMap中。實際上,WeakHashMap是通過數(shù)組table保存Entry(鍵值對);每一個Entry實際上是一個單向鏈表,即Entry是鍵值對鏈表。

(02) 當某“弱鍵”不再被其它對象引用,并被GC回收時。在GC回收該“弱鍵”時,這個“弱鍵”也同時會被添加到ReferenceQueue(queue)隊列中。

(03) 當下一次我們需要操作WeakHashMap時,會先同步table和queue。table中保存了全部的鍵值對,而queue中保存被GC回收的鍵值對;同步它們,就是刪除table中被GC回收的鍵值對。

這就是“弱鍵”如何被自動從WeakHashMap中刪除的步驟了。

和HashMap一樣,WeakHashMap是不同步的??梢允褂?Collections.synchronizedMap 方法來構(gòu)造同步的 WeakHashMap

既然有WeakHashMap,那么有WeakHashSet嗎?java collections包是沒有直接提供WeakHashSet的。

WeakHashMap的這種特性比較適合實現(xiàn)類似本地、堆內(nèi)緩存的存儲機制——緩存的失效依賴于GC收集器的行為

WeakHashMap的定義如下:

publicclass WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>

簡單來說,WeakHashMap實現(xiàn)了Map接口,基于hash-table實現(xiàn),在這種Map中,key的類型是WeakReference。如果對應的key被回收,則這個key指向的對象會被從Map容器中移除。

WeakHashMap跟普通的HashMap不同,WeakHashMap的行為一定程度上基于垃圾收集器的行為,因此一些Map數(shù)據(jù)結(jié)構(gòu)對應的常識在WeakHashMap上會失效——size()方法的返回值會隨著程序的運行變小,isEmpty()方法的返回值會從false變成true等等。

它的特殊之處在于 WeakHashMap 里的entry可能會被GC自動刪除,即使程序員沒有調(diào)用remove()或者clear()方法。

更直觀的說,當使用 WeakHashMap 時,即使沒有顯示的添加或刪除任何元素,也可能發(fā)生如下情況:

調(diào)用兩次size()方法返回不同的值;

兩次調(diào)用isEmpty()方法,第一次返回false,第二次返回true;

兩次調(diào)用containsKey()方法,第一次返回true,第二次返回false,盡管兩次使用的是同一個key;

兩次調(diào)用get()方法,第一次返回一個value,第二次返回null,盡管兩次使用的是同一個對象。

遇到這么奇葩的現(xiàn)象,你是不是覺得使用者一定會瘋掉?其實不然,WeekHashMap 的這個特點特別適用于需要緩存的場景。在緩存場景下,由于內(nèi)存是有限的,不能緩存所有對象;對象緩存命中可以提高系統(tǒng)效率,但緩存MISS也不會造成錯誤,因為可以通過計算重新得到。

要明白 WeekHashMap 的工作原理,還需要引入一個概念:弱引用(WeakReference)。我們都知道Java中內(nèi)存是通過GC自動管理的,GC會在程序運行過程中自動判斷哪些對象是可以被回收的,并在合適的時機進行內(nèi)存釋放。GC判斷某個對象是否可被回收的依據(jù)是,是否有有效的引用指向該對象。如果沒有有效引用指向該對象(基本意味著不存在訪問該對象的方式),那么該對象就是可回收的。這里的**“有效引用”并不包括弱引用**。也就是說,雖然弱引用可以用來訪問對象,但進行垃圾回收時弱引用并不會被考慮在內(nèi),僅有弱引用指向的對象仍然會被GC回收。

WeakHashMap 內(nèi)部是通過弱引用來管理entry的,弱引用的特性對應到 WeakHashMap 上意味著什么呢?將一對key, value放入到 WeakHashMap 里并不能避免該key值被GC回收,除非在 WeakHashMap 之外還有對該key的強引用。

關于強引用,弱引用等概念以后再具體講解,這里只需要知道Java中引用也是分種類的,并且不同種類的引用對GC的影響不同就夠了。

具體實現(xiàn)

Weak HashSet?

如果你看過前幾篇關于 Map 和 Set 的講解,一定會問:既然有 WeekHashMap,是否有 WeekHashSet 呢?答案是沒有===不過Java Collections工具類給出了解決方案,Collections.newSetFromMap(Map<E,Boolean> map)方法可以將任何 Map包裝成一個Set。通過如下方式可以快速得到一個 Weak HashSet:

// 將WeakHashMap包裝成一個Set
Set<Object> weakHashSet = Collections.newSetFromMap(
? ? ? ?new WeakHashMap<Object, Boolean>());

不出你所料,newSetFromMap()方法只是對傳入的 Map做了簡單包裝:

// Collections.newSetFromMap()用于將任何Map包裝成一個Set
publicstatic <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
? ?returnnew SetFromMap<>(map);
}

privatestaticclass SetFromMap<E> extends AbstractSet<E>
? ?implements Set<E>, Serializable
{
? ?privatefinal Map<E, Boolean> m; ?// The backing map
? ?privatetransient Set<E> s; ? ? ? // Its keySet
? ?SetFromMap(Map<E, Boolean> map) {
? ? ? ?if (!map.isEmpty())
? ? ? ? ? ?thrownew IllegalArgumentException("Map is non-empty");
? ? ? ?m = map;
? ? ? ?s = map.keySet();
? ?}
? ?public void clear() ? ? ? ? ? ? ? { ? ? ? ?m.clear(); }
? ?public int size() ? ? ? ? ? ? ? ? { return m.size(); }
? ?public boolean isEmpty() ? ? ? ? ?{ return m.isEmpty(); }
? ?public boolean contains(Object o) { return m.containsKey(o); }
? ?public boolean remove(Object o) ? { return m.remove(o) != null; }
? ?public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
? ?public Iterator<E> iterator() ? ? { return s.iterator(); }
? ?public Object[] toArray() ? ? ? ? { return s.toArray(); }
? ?public <T> T[] toArray(T[] a) ? ? { return s.toArray(a); }
? ?public String toString() ? ? ? ? ?{ return s.toString(); }
? ?public int hashCode() ? ? ? ? ? ? { return s.hashCode(); }
? ?public boolean equals(Object o) ? { return o == this || s.equals(o); }
? ?public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
? ?public boolean removeAll(Collection<?> c) ? {return s.removeAll(c);}
? ?public boolean retainAll(Collection<?> c) ? {return s.retainAll(c);}
? ?// addAll is the only inherited implementation
? ?......
}

實例:

此例子中聲明了兩個Map對象,一個是HashMap,一個是WeakHashMap,同時向兩個map中放入a、b兩個對象,當HashMap remove掉a 并且將a、b都指向null時,WeakHashMap中的a將自動被回收掉。

出現(xiàn)這個狀況的原因是,對于a對象而言,當HashMap remove掉并且將a指向null后,除了WeakHashMap中還保存a外已經(jīng)沒有指向a的指針了,所以WeakHashMap會自動舍棄掉a,而對于b對象雖然指向了null,但HashMap中還有指向b的指針。

弱引用(WeakReference)的特性是:當gc線程發(fā)現(xiàn)某個對象只有弱引用指向它,那么就會將其銷毀并回收內(nèi)存。

package com.sino.daily.code_2019_9_1;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;

/**
* create by 2020-04-28 22:45
*
* @author liulq
*/
publicclass WeekHash {
? ?public static void main(String[] args) {
? ? ? ?String a = new String("a");
? ? ? ?String b = new String("b");
? ? ? ?Map map = new HashMap();
? ? ? ?map.put(a, "aaa");
? ? ? ?map.put(b, "bbb");

? ? ? ?Map weakmap = new WeakHashMap();
? ? ? ?weakmap.put(a, "aaa");
? ? ? ?weakmap.put(b, "bbb");

? ? ? ?map.remove(a);
? ? ? ?a = null;
? ? ? ?b = null;

? ? ? ?System.gc();
? ? ? ?
? ? ? ?Iterator i = map.entrySet().iterator();
? ? ? ?while (i.hasNext()) {
? ? ? ? ? ?Map.Entry en = (Map.Entry) i.next();
? ? ? ? ? ?System.out.println("map:" + en.getKey() + ":" + en.getValue());
? ? ? ?}

? ? ? ?Iterator j = weakmap.entrySet().iterator();
? ? ? ?while (j.hasNext()) {
? ? ? ? ? ?Map.Entry en = (Map.Entry) j.next();
? ? ? ? ? ?System.out.println("weakmap:" + en.getKey() + ":" + en.getValue());

? ? ? ?}
? ?}
}

經(jīng)典使用場景:tomcat兩級緩存

tomcat的源碼里,實現(xiàn)緩存時會用到WeakHashMap

package org.apache.tomcat.util.collections;

import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

publicfinalclass ConcurrentCache<K,V> {

? ?privatefinalint size;

? ?privatefinal Map<K,V> eden;

? ?privatefinal Map<K,V> longterm;

? ?public ConcurrentCache(int size) {
? ? ? ?this.size = size;
? ? ? ?this.eden = new ConcurrentHashMap<>(size);
? ? ? ?this.longterm = new WeakHashMap<>(size);
? ?}

? ?public V get(K k) {
? ? ? ?V v = this.eden.get(k);
? ? ? ?if (v == null) {
? ? ? ? ? ?synchronized (longterm) {
? ? ? ? ? ? ? ?v = this.longterm.get(k);
? ? ? ? ? ?}
? ? ? ? ? ?if (v != null) {
? ? ? ? ? ? ? ?this.eden.put(k, v);
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?return v;
? ?}

? ?public void put(K k, V v) {
? ? ? ?if (this.eden.size() >= size) {
? ? ? ? ? ?synchronized (longterm) {
? ? ? ? ? ? ? ?this.longterm.putAll(this.eden);
? ? ? ? ? ?}
? ? ? ? ? ?this.eden.clear();
? ? ? ?}
? ? ? ?this.eden.put(k, v);
? ?}
}

源碼中有edenlongterm的兩個map,對jvm堆區(qū)有所了解的話,可以猜測出tomcat在這里是使用ConcurrentHashMapWeakHashMap做了分代的緩存。

在put方法里,在插入一個k-v時,先檢查eden緩存的容量是不是超了。沒有超就直接放入eden緩存,如果超了則鎖定longterm將eden中所有的k-v都放入longterm。再將eden清空并插入k-v。在get方法中,也是優(yōu)先從eden中找對應的v,如果沒有則進入longterm緩存中查找,找到后就加入eden緩存并返回。

經(jīng)過這樣的設計,相對常用的對象都能在eden緩存中找到,不常用(有可能被銷毀的對象)的則進入longterm緩存。而longterm的key的實際對象沒有其他引用指向它時,gc就會自動回收heap中該弱引用指向的實際對象,弱引用進入引用隊列。longterm調(diào)用expungeStaleEntries()方法,遍歷引用隊列中的弱引用,并清除對應的Entry,不會造成內(nèi)存空間的浪費。

總結(jié):

    1. WeakHashMap還是集合,但是在開發(fā)中也很少用到這種集合,也只有底層源碼才能用到這些。

    2. 知道四種引用:

    強引用

    如果一個對象具有強引用,它就不會被垃圾回收器回收。即使當前內(nèi)存空間不足,JVM也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。比如String str = "hello"這時候str就是一個強引用。

    軟引用

    內(nèi)存足夠的時候,軟引用對象不會被回收,只有在內(nèi)存不足時,系統(tǒng)則會回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。

    弱引用

    如果一個對象具有弱引用,在垃圾回收時候,一旦發(fā)現(xiàn)弱引用對象,無論當前內(nèi)存空間是否充足,都會將弱引用回收。

    虛引用

    如果一個對象具有虛引用,就相當于沒有引用,在任何時候都有可能被回收。使用虛引用的目的就是為了得知對象被GC的時機,所以可以利用虛引用來進行銷毀前的一些操作,比如說資源釋放等。

    我們的WeakHashMap是基于弱引用的,也就是說只要垃圾回收機制一開啟,就直接開始了掃蕩,看見了就清除。


    why?什么需要WeakHashMap:WeakHashMap正是由于使用的是弱引用,因此它的對象可能被隨時回收。更直觀的說,當使用 WeakHashMap 時,即使沒有刪除任何元素,它的尺寸、get方法也可能不一樣,適用于緩存的場景

    3.這個集合雖然現(xiàn)在不是很能理解他,但是他的實現(xiàn)原理和設計原理值得我們學習,希望在我們技術(shù)的進階中掌握這種設計和實現(xiàn)

    4.都是站在巨人的肩膀上前進,多學習,多總結(jié),多自己總結(jié)經(jīng)驗,多敲代碼,自律,鍛煉身體,其他的都交給時間來檢驗成果吧!





Java十三篇:WeakHashMap的評論 (共 條)

分享到微博請遵守國家法律
灵石县| 全南县| 金坛市| 榆林市| 西安市| 剑河县| 邛崃市| 平江县| 仙桃市| 建宁县| 松原市| 习水县| 灌阳县| 庆元县| 晋中市| 长丰县| 沅江市| 朝阳市| 淮北市| 铜山县| 延川县| 北安市| 错那县| 静宁县| 西乌珠穆沁旗| 和顺县| 三河市| 金溪县| 康乐县| 吉木萨尔县| 木兰县| 静安区| 西青区| 渭南市| 永福县| 微博| 万源市| 许昌市| 秀山| 峡江县| 准格尔旗|