欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java之WeakHashMap源碼淺析

 更新時(shí)間:2023年09月07日 09:50:57   作者:澄風(fēng)  
這篇文章主要介紹了Java之WeakHashMap源碼淺析,WeakHashMap從名字可以得知主要和Map有關(guān),不過還有一個(gè)Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用,需要的朋友可以參考下

定義

從名字可以得知主要和Map有關(guān),不過還有一個(gè)Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用。

  • 強(qiáng)引用:
    • 如果一個(gè)對(duì)象具有強(qiáng)引用,它就不會(huì)被垃圾回收器回收。即使當(dāng)前內(nèi)存空間不足,JVM也不會(huì)回收它,而是拋出 OutOfMemoryError 錯(cuò)誤,使程序異常終止。 比如String str = "hello"這時(shí)候str就是一個(gè)強(qiáng)引用。
  • 軟引用:
    • 內(nèi)存足夠的時(shí)候,軟引用對(duì)象不會(huì)被回收,只有在內(nèi)存不足時(shí),系統(tǒng)則會(huì)回收軟引用對(duì)象,如果回收了軟引用對(duì)象之后仍然沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。
  • 弱引用:
    • 如果一個(gè)對(duì)象具有弱引用,在垃圾回收時(shí)候,一旦發(fā)現(xiàn)弱引用對(duì)象,無論當(dāng)前內(nèi)存空間是否充足,都會(huì)將弱引用回收。
  • 虛引用:
    • 如果一個(gè)對(duì)象具有虛引用,就相當(dāng)于沒有引用,在任何時(shí)候都有可能被回收。 使用虛引用的目的就是為了得知對(duì)象被GC的時(shí)機(jī),所以可以利用虛引用來進(jìn)行銷毀前的一些操作,比如說資源釋放等。

源碼解析

看下WeakHashMap 的構(gòu)造函數(shù)

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;
    // 保證容量是2的整數(shù)倍,有助于hash運(yùn)算
    while (capacity < initialCapacity)
        capacity <<= 1;
    // 初始化table數(shù)組
    table = newTable(capacity);
    this.loadFactor = loadFactor;
    // 閥值
    threshold = (int)(capacity * loadFactor);
}

沒什么好說的 table 是一個(gè) Entry數(shù)組 Entry<K,V>[] table; newTable會(huì)初始化一個(gè)數(shù)組數(shù)組的容量就是前面計(jì)算出來的capacity,其值為2的整數(shù)次方。

HashMap的容量為什么是2的n次方?HashMap是如何保證容量是2的n次方的? HashMap容量取2的n次方,主要與hash尋址有關(guān)。在put(key,value)時(shí),putVal()方法中通過i = (n - 1) & hash來計(jì)算key的散列地址。其實(shí),i = (n - 1) & hash是一個(gè)%操作。也就是說,HashMap是通過%運(yùn)算來獲得key的散列地址的。但是,%運(yùn)算的速度并沒有&的操作速度快。而&操作能代替%運(yùn)算,必須滿足一定的條件,也就是a%b=a&(b-1)僅當(dāng)b是2的n次方的時(shí)候方能成立。這也就是為什么HashMap的容量需要保持在2的n次方了。

再看下Entry的類定義

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    final int hash;
    Entry<K,V> next;
    /**
     * Creates new entry.
     */
    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;
    }
    ...
}

可以看到Entry是繼承WeakReference的,我們結(jié)合WeakReference再看一下:

public class WeakReference<T> extends Reference<T> {
    /**
     * Creates a new weak reference that refers to the given object.  The new
     * reference is not registered with any queue.
     * 創(chuàng)建一個(gè)新的弱應(yīng)用給傳入的對(duì)象,這個(gè)新的引用不注冊(cè)任何隊(duì)列
     *
     * @param referent object the new weak reference will refer to
     */
    public WeakReference(T referent) {
        super(referent);
    }
    /**
     * Creates a new weak reference that refers to the given object and is
     * registered with the given queue.
     * 創(chuàng)建一個(gè)新的弱應(yīng)用給傳入的對(duì)象,這個(gè)新的引用注冊(cè)給一個(gè)給定的隊(duì)列
     *
     * @param referent object the new weak reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}

我們發(fā)現(xiàn)在 weakhashmap 中把key注冊(cè)給了 WeakReference ,也就是說在 WeakHashMap 中key是一個(gè)弱引用。但這個(gè)queue是什么我們接著看,在往 WeakHashMap 中put一個(gè)元素的時(shí)候,會(huì)創(chuàng)建Entry。再看 WeakHashMap 的put操作,我們?nèi)绻煜?HashMap 其實(shí)我們不需要怎么看這部分的代碼,無非是計(jì)算hash值,散列分布到數(shù)組的各個(gè)位置,如果 hash 沖突使用拉鏈法進(jìn)行解決。這里和hashmap有一點(diǎn)不一樣的是hashmap如果鏈長達(dá)到閥值會(huì)使用紅黑樹。

public V put(K key, V value) {
    // 如果key是null則給定一個(gè)空的對(duì)象進(jìn)行修飾
    Object k = maskNull(key);
    // 計(jì)算key的hash
    int h = hash(k);
    // 獲取table
    Entry<K,V>[] tab = getTable();
    // 根據(jù)hash找到數(shù)組下標(biāo)
    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) // 是否達(dá)到閥值達(dá)到閥值就擴(kuò)容
        resize(tab.length * 2);
    return null;
}

可以看到這個(gè)queue是一個(gè)實(shí)例化final修飾的屬性。

private final ReferenceQueue<Object> queue = new ReferenceQueue<>();

再看下getTable是什么情況,看源碼會(huì)知道所有的WeekHashMap的所有操作都要調(diào)用 getTable -> expungeStaleEntries

private Entry<K,V>[] getTable() {
    expungeStaleEntries();
    return table;
}

我們看下expungeStaleEntries做了哪些事情?

private void expungeStaleEntries() {
    // 從 ReferenceQueue中拉取元素
    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
                    // 拿到entry的值賦值為null幫助GC
                    e.value = null; // Help GC
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }
}

expungeStaleEntries 就是WeakHashMap的核心了,它承擔(dān)著Map中死對(duì)象的清理工作。原理就是依賴WeakReference和ReferenceQueue的特性。

在每個(gè)WeakHashMap都有個(gè)ReferenceQueue queue,在Entry初始化的時(shí)候也會(huì)將queue傳給WeakReference,這樣當(dāng)某個(gè)可以key失去所有強(qiáng)應(yīng)用之后,其key對(duì)應(yīng)的WeakReference對(duì)象會(huì)被放到queue里,有了queue就知道需要清理哪些Entry了。

這里也是整個(gè)WeakHashMap里唯一加了同步的地方。除了上文說的到resize中調(diào)用了expungeStaleEntries(),size()中也調(diào)用了這個(gè)清理方法。另外 getTable()也調(diào)了,這就意味著幾乎所有其他方法都間接調(diào)用了清理。

WeakHashMap的一點(diǎn)點(diǎn)缺點(diǎn)

提到缺點(diǎn)我不太認(rèn)為是缺點(diǎn),在某種場景下缺點(diǎn)也有可能是優(yōu)點(diǎn),而且很多缺點(diǎn)也是可以彌補(bǔ)的。

但非要說個(gè)一二三,這里列出下面兩種:

1.非線程安全

關(guān)鍵修改方法沒有提供任何同步,多線程環(huán)境下肯定會(huì)導(dǎo)致數(shù)據(jù)不一致的情況,所以使用時(shí)需要多注意。

2.單純作為Map沒有HashMap好

HashMap在Jdk8做了好多優(yōu)化,比如單鏈表在過長時(shí)會(huì)轉(zhuǎn)化為紅黑樹,降低極端情況下的操作復(fù)雜度。但WeakHashMap沒有相應(yīng)的優(yōu)化,有點(diǎn)像jdk8之前的HashMap版本。

WeakHashMap可以應(yīng)用的地方

1.緩存

2.診斷工具,比如atlas,將字節(jié)碼緩存放入到WeakHashMap中

到此這篇關(guān)于Java之WeakHashMap源碼淺析的文章就介紹到這了,更多相關(guān)WeakHashMap源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼

    SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼

    這篇文章主要介紹了SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java 獲取本機(jī)的IP與MAC地址實(shí)現(xiàn)詳解

    Java 獲取本機(jī)的IP與MAC地址實(shí)現(xiàn)詳解

    這篇文章主要介紹了Java 獲取本機(jī)的IP與MAC地址實(shí)現(xiàn)詳解的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • 教你用java?stream對(duì)集合中的對(duì)象按指定字段進(jìn)行分組并統(tǒng)計(jì)

    教你用java?stream對(duì)集合中的對(duì)象按指定字段進(jìn)行分組并統(tǒng)計(jì)

    這篇文章主要給大家介紹了關(guān)于用java?stream對(duì)集合中的對(duì)象按指定字段進(jìn)行分組并統(tǒng)計(jì)的相關(guān)資料,本文主要介紹了如何利用Java的Stream流來實(shí)現(xiàn)在list集合中,對(duì)具有相同name屬性的對(duì)象進(jìn)行匯總計(jì)算的需求,需要的朋友可以參考下
    2024-10-10
  • Java?properties?和?yml?的區(qū)別解析

    Java?properties?和?yml?的區(qū)別解析

    properties和yml都是Spring?Boot支持的兩種配置文件,它們可以看做Spring?Boot在不同時(shí)期的兩種“產(chǎn)品”,這篇文章主要介紹了Java?properties?和?yml?的區(qū)別,需要的朋友可以參考下
    2023-02-02
  • spring(java,js,html) 截圖上傳圖片實(shí)例詳解

    spring(java,js,html) 截圖上傳圖片實(shí)例詳解

    這篇文章主要介紹了spring(java,js,html) 截圖上傳圖片實(shí)例詳解的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • Spring Boot集成starrocks快速入門Demo(適用場景)

    Spring Boot集成starrocks快速入門Demo(適用場景)

    StarRocks 是新一代極速全場景 MPP (Massively Parallel Processing) 數(shù)據(jù)庫,StarRocks 的愿景是能夠讓用戶的數(shù)據(jù)分析變得更加簡單和敏捷,這篇文章主要介紹了Spring Boot集成starrocks快速入門Demo,需要的朋友可以參考下
    2024-08-08
  • java和matlab畫多邊形閉合折線圖示例講解

    java和matlab畫多邊形閉合折線圖示例講解

    由于要將“哈密頓回路問題(TSP)”的求解中間結(jié)果表示出來,查了一下使用程序畫多邊形圖形?,F(xiàn)在在總結(jié)一下,這個(gè)圖是“由給定節(jié)點(diǎn)首尾相連的”閉合多邊形
    2014-02-02
  • 關(guān)于Mybatis 中使用Mysql存儲(chǔ)過程的方法

    關(guān)于Mybatis 中使用Mysql存儲(chǔ)過程的方法

    這篇文章給大家介紹了Mybatis 中使用Mysql存儲(chǔ)過程的方法,本文通過實(shí)例代碼相結(jié)合的形式給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2018-03-03
  • Java進(jìn)程cpu頻繁100%問題解決方案

    Java進(jìn)程cpu頻繁100%問題解決方案

    這篇文章主要介紹了Java進(jìn)程cpu頻繁100%問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Netty分布式pipeline管道傳播事件的邏輯總結(jié)分析

    Netty分布式pipeline管道傳播事件的邏輯總結(jié)分析

    這篇文章主要為大家介紹了Netty分布式pipeline管道傳播事件總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03

最新評(píng)論