最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中WeakHashMap的弱鍵回收機制

 更新時間:2023年09月07日 09:21:00   作者:Tony-老師  
這篇文章主要介紹了Java中WeakHashMap的弱鍵回收機制,WeakHashMap繼承AbstractMap,實現(xiàn)了Map接口,和HashMap一樣,WeakHashMap也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以是null,需要的朋友可以參考下

1. WeakHashMap介紹

WeakHashMap繼承AbstractMap,實現(xiàn)了Map接口。

和HashMap一樣,WeakHashMap也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以是null。

 不過WeakHashMap的鍵是"弱鍵"。在WeakHashMap中,當某個鍵不再正常使用時,會被從WeakHashMap中被自動移除。

更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。某個鍵被終止時,它對應(yīng)的鍵值對也就從映射中有效地移除了。

WeakHashMap內(nèi)部是通過弱引用來管理 entry 的,弱引用的特性對應(yīng)到WeakHashMap上意味著什么呢?

將一對 key, value 放入到WeakHashMap里并不能避免該 key 值被GC回收,除非在WeakHashMap之外還有對該 key 的強引用。

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

2. WeakHashMap例子

public class TestWeakHashMap {
    public static void main(String[] args) {
        WeakHashMap<String, String> weakHashMap = new WeakHashMap<>(10);
        String key0 = new String("kuang");
        String key1 = new String("zhong");
        String key2 = new String("wen");
        // 存放元素
        weakHashMap.put(key0, "q1");
        weakHashMap.put(key1, "q2");
        weakHashMap.put(key2, "q3");
        System.out.printf("weakHashMap: %s\n", weakHashMap);
        // 是否包含某key
        System.out.printf("contains key kuang : %s\n", weakHashMap.containsKey(key0));
        System.out.printf("contains key zhong : %s\n", weakHashMap.containsKey(key1));
        // 是否包含某value
        System.out.printf("contains value 0 : %s\n", weakHashMap.containsValue(0));
        // 移除key
        weakHashMap.remove(key2);
        System.out.printf("weakHashMap after remove: %s", weakHashMap);
        // 這意味著"弱鍵"key0再沒有被其它對象引用,調(diào)用gc時會回收WeakHashMap中與key0對應(yīng)的鍵值對
        key0 = null;
        // 內(nèi)存回收,這里會回收WeakHashMap中與"key0"對應(yīng)的鍵值對
        System.gc();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 遍歷WeakHashMap
        Iterator iter = weakHashMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry en = (Map.Entry) iter.next();
            System.out.printf("next : %s - %s\n", en.getKey(), en.getValue());
        }
        // 打印WeakHashMap的實際大小
        System.out.printf("after gc WeakHashMap size: %s\n", weakHashMap.size());
    }
}

執(zhí)行輸出:

weakHashMap: {wen=q3, zhong=q2, kuang=q1}
contains key kuang : true
contains key zhong : true
contains value 0 : false
weakHashMap after remove: {zhong=q2, kuang=q1}next : zhong - q2
after gc WeakHashMap size: 1

上面的例子展示了WeakHashMap的增刪改查,以及弱鍵的回收,可以看到把Key的引用置為null,gc后,會將該鍵值對回收。

3. WeakHashMap的使用場景

一般用做緩存,比如Tomcat的源碼里,實現(xiàn)緩存時會用到WeakHashMap,在緩存系統(tǒng)中,使用WeakHashMap可以避免內(nèi)存泄漏,但是使用WeakHashMap做緩存時要注意,如果只有它的key只有WeakHashMap本身在用,而在WeakHashMap之外沒有對該 key 的強引用,那么GC時會回收這個key對應(yīng)的entry。所以WeakHashMap不能用做主緩存,合適的用法應(yīng)該是用它做二級的內(nèi)存緩存,即那么過期緩存數(shù)據(jù)或者低頻緩存數(shù)據(jù)。

public final class ConcurrentCache<K,V> {
    private final int size;
    private final Map<K,V> eden;
    private final 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);
    }
}

源碼中有eden和longterm的兩個map,對jvm堆區(qū)有所了解的話,可以猜測出tomcat在這里是使用ConcurrentHashMap和WeakHashMap做了分代的緩存。在put方法里,在插入一個k-v時,先檢查eden緩存的容量是不是超了。沒有超就直接放入eden緩存,如果超了則鎖定longterm將eden中所有的k-v都放入longterm。再將eden清空并插入k-v。在get方法中,也是優(yōu)先從eden中找對應(yīng)的v,如果沒有則進入longterm緩存中查找,找到后就加入eden緩存并返回。 

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

4. WeakHashMap的數(shù)據(jù)結(jié)構(gòu)

前面已經(jīng)大概了解了WeakHashMap,接下來來分析WeakHashMap的源碼,先從它的數(shù)據(jù)結(jié)構(gòu)開始。

4.1 類的定義

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V> {
}
java.lang.Object
   ?     java.util.AbstractMap<K, V>
         ?     java.util.WeakHashMap<K, V>

4.2 變量與常量

    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    Entry<K,V>[] table;
    private int size;
    private int threshold;
    private final float loadFactor;
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    int modCount;
  • DEFAULT_INITIAL_CAPACITY :   初始容量
  • MAXIMUM_CAPACITY :   最大容量
  • DEFAULT_LOAD_FACTOR :   默認加載因子
  • table :   Entry數(shù)組
  • size :   實際存放的數(shù)據(jù)個數(shù)
  • threshold :   擴容閾值
  • loadFactor :   加載因子
  • queue :   引用隊列
  • modCount : 修改次數(shù)

4.3 Entry類

   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) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
        @SuppressWarnings("unchecked")
        public K getKey() {
            return (K) WeakHashMap.unmaskNull(get());
        }
        public V getValue() {
            return value;
        }
        public V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        ...
    }

可以看到Entry類實現(xiàn)了Map.Entry接口,繼承弱引用(WeakReference),屬性有key,value和next引用。

構(gòu)造器中需要傳入一個引用隊列,方法主要看getKey():

return (K) WeakHashMap.unmaskNull(get());

再來看看WeakHashMap的靜態(tài)方法unmaskNull():

    private static final Object NULL_KEY = new Object();
    static Object unmaskNull(Object key) {
        return (key == NULL_KEY) ? null : key;
    }

判斷key是否等于NULL_KEY來選擇是否返回null。

4.4 類關(guān)系圖

5. WeakHashMap的弱鍵回收

上面看完WeakHashMap的數(shù)據(jù)結(jié)構(gòu),那么WeakHashMap是如何實現(xiàn)弱鍵回收的呢?其實根據(jù)前面的文章也能猜到,利用Reference和ReferenceQueue。

5.1 put數(shù)據(jù)

    public V put(K key, V value) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);
        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        modCount++;
        Entry<K,V> e = tab[i];
        tab[i] = new Entry<>(k, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }
   private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }
  • 判斷key是否為null,為null的話將key賦值為NULL_KEY。
  • 計算key的hash值,然后根據(jù)hash值查找待插入的位置。
  • 遍歷Entry數(shù)組,看該鍵是否已存在,存在的話則替換舊值,并返回舊值。
  • 不存在則構(gòu)建Entry對象存入數(shù)組。

這個流程和HashMap,HashTable等差不多。

5.2 get數(shù)據(jù)

    public V get(Object key) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int index = indexFor(h, tab.length);
        Entry<K,V> e = tab[index];
        while (e != null) {
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        return null;
    }

一句話,先根據(jù)key的hash值找到索引位置,然后拿到Entry對象,再判斷該Entry是否存在下一個引用(即hash碰撞),遍歷該單鏈表,比較value值。

5.3 弱鍵如何回收?

根據(jù)上面的分析就很容易得出了,WeakHashMap內(nèi)部的數(shù)據(jù)存儲是用Entry[]數(shù)組,即鍵值對數(shù)組。

Entry類繼承于WeakReferece(弱引用),弱引用的特點再重申下:當JVM進行垃圾回收時,無論內(nèi)存是否充足,都會回收被弱引用關(guān)聯(lián)的對象。

在構(gòu)造一個Entry對象的時候,會傳入一個ReferenceQueue,key為弱引用包裹的對象:

    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

再來看看WeakHashMap如何清理已經(jīng)被回收的key的,被回收的key會存放在引用隊列中:

private void expungeStaleEntries() {
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                int i = indexFor(e.hash, table.length);
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) {
                        if (prev == e)
                            table[i] = next;
                        else
                            prev.next = next;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }

遍歷引用隊列,然后刪除已被回收的鍵值對(從數(shù)組移除,改變單鏈表結(jié)點引用,將value賦值為null),該方法會在WeakHashMap增刪改查、擴容的地方調(diào)用。

因為一個key-value存放到WeakHashMap中后,key會被用弱引用包起來存儲,如果這個key在WeakHashMap外部沒有強引用的話,GC時將被回收,然后WeakHashMap根據(jù)引用隊列對已回收的key做清理。

到此這篇關(guān)于Java中WeakHashMap的弱鍵回收機制的文章就介紹到這了,更多相關(guān)WeakHashMap弱鍵回收內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實現(xiàn)文件下載的限速功能

    SpringBoot實現(xiàn)文件下載的限速功能

    在SpringBoot項目中,實現(xiàn)文件下載的限速功能可以有效控制服務(wù)器帶寬的占用,并防止單個用戶消耗過多的資源,本文將通過具體的代碼示例和詳細的流程解釋,介紹如何在SpringBoot項目中實現(xiàn)文件下載的限速功能,需要的朋友可以參考下
    2024-07-07
  • Idea中如何調(diào)出Run dashboard 或services窗口

    Idea中如何調(diào)出Run dashboard 或services窗口

    這篇文章主要介紹了Idea中如何調(diào)出Run dashboard 或services窗口問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java通過HttpClient進行HTTP請求的代碼詳解

    Java通過HttpClient進行HTTP請求的代碼詳解

    Apache?HttpClient是一個功能強大且廣泛使用的Java庫,它提供了方便的方法來執(zhí)行HTTP請求并處理響應(yīng)。本文將介紹如何使用HttpClient庫進行HTTP請求,包括GET請求、POST請求、添加參數(shù)和請求體、設(shè)置請求頭等操作,需要的朋友可以參考下
    2023-05-05
  • Java中eq、ne、ge、gt、le、lt的含義詳細解釋

    Java中eq、ne、ge、gt、le、lt的含義詳細解釋

    Java中的比較運算符包括eq(等于)、ne(不等于)、ge(大于或等于)、gt(大于)、le(小于或等于)和lt(小于),這些運算符在控制流語句和條件語句中用于判斷條件是否滿足,從而決定程序的執(zhí)行路徑,需要的朋友可以參考下
    2024-11-11
  • java開發(fā)主流定時任務(wù)解決方案全橫評詳解

    java開發(fā)主流定時任務(wù)解決方案全橫評詳解

    這篇文章主要為大家介紹了java開發(fā)主流定時任務(wù)解決方案全橫評詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • java編程中實現(xiàn)調(diào)用js方法分析

    java編程中實現(xiàn)調(diào)用js方法分析

    這篇文章主要介紹了java編程中實現(xiàn)調(diào)用js方法,結(jié)合具體實例形式較為詳細的分析了java編程中調(diào)用js方法的常用操作技巧與注意事項,需要的朋友可以參考下
    2017-09-09
  • java的反射用不好試試內(nèi)省?

    java的反射用不好試試內(nèi)省?

    使用內(nèi)省相對于直接使用反射更加安全可靠,Java的反射機制比較特殊,它不同于一般的編程方式,稍不小心就容易破壞類的封裝性。練的不好,就容易走火入魔。沒關(guān)系,很多時候我們還可以使用Java的內(nèi)省機制哦
    2021-07-07
  • Java正則表達式的替換和分組功能

    Java正則表達式的替換和分組功能

    這篇文章主要給大家介紹了關(guān)于Java正則表達式的替換和分組功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • JAVA項目如何打包部署到Linux服務(wù)器上

    JAVA項目如何打包部署到Linux服務(wù)器上

    本文詳細介紹了在服務(wù)器上部署環(huán)境包括JDK、MySQL、Tomcat的設(shè)置,以及使用Idea-Maven-SpringBoot進行jar包打包部署的流程,內(nèi)容涵蓋了MySQL配置注意事項、pom.xml配置、打包命令等關(guān)鍵步驟,同時,也提供了如何將jar包上傳到Linux服務(wù)器并運行的具體方法
    2024-10-10
  • Spring 整合Shiro 并擴展使用EL表達式的實例詳解

    Spring 整合Shiro 并擴展使用EL表達式的實例詳解

    Shiro是一個輕量級的權(quán)限控制框架,應(yīng)用非常廣泛。本文的重點是介紹Spring整合Shiro,并通過擴展使用Spring的EL表達式。需要的朋友可以參考下
    2018-03-03

最新評論

新龙县| 江都市| 罗定市| 和硕县| 藁城市| 萝北县| 渑池县| 霍山县| 文成县| 桓仁| 泽普县| 山丹县| 镇雄县| 辽阳县| 舞阳县| 甘泉县| 涞源县| 新宁县| 织金县| 和政县| 曲松县| 彭泽县| 吉安县| 新郑市| 襄汾县| 河曲县| 阳春市| 普定县| 彰化县| 台北县| 昭苏县| 新乡县| 丹江口市| 万载县| 郧西县| 静乐县| 读书| 陕西省| 琼海市| 苏尼特右旗| 苍梧县|