亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的WeakHashMap源碼分析

 更新時間:2023年09月06日 09:24:49   作者:weixin_30692143  
這篇文章主要介紹了Java中的WeakHashMap源碼分析,WeakHashMap可能平時使用的頻率并不高,但是你可能聽過WeakHashMap會進(jìn)行自動回收吧,下面就對其原理進(jìn)行分析,需要的朋友可以參考下

1.WeakHashMap介紹

WeakHashMap是一種弱引用的map,底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組+鏈表,內(nèi)部的key存儲為弱引用,在GC時如果key不存在強(qiáng)引用的情況下會被回收掉,而對于value的回收會在下一次操作map時回收掉,所以WeakHashMap適合緩存處理。

1 java.lang.Object
2        java.util.AbstractMap<K, V>
3              java.util.WeakHashMap<K, V>
4 
5 public class WeakHashMap<K,V>
6     extends AbstractMap<K,V>
7     implements Map<K,V> {}

從WeakHashMap的繼承關(guān)系上來看,可知其繼承AbstractMap,實(shí)現(xiàn)了Map接口。

其底層數(shù)據(jù)結(jié)構(gòu)是Entry數(shù)組,Entry的數(shù)據(jù)結(jié)構(gòu)如下:

從源碼上可知,Entry的內(nèi)部并沒有存儲key的值,而是通過調(diào)用父類的構(gòu)造方法,傳入key和ReferenceQueue,最終key和queue會關(guān)聯(lián)到Reference中

這里是GC時,清清除key的關(guān)鍵,這里大致看下Reference的源碼:

private static class ReferenceHandler extends Thread {
        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }
        static {
            // pre-load and initialize InterruptedException and Cleaner classes
            // so that we don't get into trouble later in the run loop if there's
            // memory shortage while loading/initializing them lazily.
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }
        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }
        public void run() {
            // 注意這里為一個死循環(huán)
            while (true) {
                tryHandlePending(true);
            }
        }
    }
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    // 'instanceof' might throw OutOfMemoryError sometimes
                    // so do this before un-linking 'r' from the 'pending' chain...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // The waiting on the lock may cause an OutOfMemoryError
                    // because it may try to allocate exception objects.
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // Give other threads CPU time so they hopefully drop some live references
            // and GC reclaims some space.
            // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
            // persistently throws OOME for some time...
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }
        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }
        // 加入對列
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }
    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        // 創(chuàng)建handler
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        // 線程優(yōu)先級最大
        handler.setPriority(Thread.MAX_PRIORITY);
        // 設(shè)置為守護(hù)線程
        handler.setDaemon(true);
        handler.start();
        // provide access in SharedSecrets
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

通過查看Reference源碼可知,在實(shí)例化時會創(chuàng)建一個守護(hù)線程,然后不斷循環(huán)將GC時的Entry入隊(duì),關(guān)于如何清除value值的下面會進(jìn)行分析。

2.具體源碼分析

put操作:

public V put(K key, V value) {
        // 確定key值,允許key為null
        Object k = maskNull(key);
        // 獲取器hash值
        int h = hash(k);
        // 獲取tab
        Entry<K,V>[] tab = getTable();
        // 確定在tab中的位置 簡單的&操作
        int i = indexFor(h, tab.length);
        // 遍歷,是否要進(jìn)行覆蓋操作
        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;
            }
        }
        // 修改次數(shù)自增
        modCount++;
        // 取出i上的元素
        Entry<K,V> e = tab[i];
        // 構(gòu)建鏈表,新元素在鏈表頭
        tab[i] = new Entry<>(k, value, queue, h, e);
        // 檢查是否需要擴(kuò)容
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

分析:

WeakHashMap的put操作與HashMap相似,都會進(jìn)行覆蓋操作(相同key),但是注意插入新節(jié)點(diǎn)是放在鏈表頭。

上述代碼中還要一個關(guān)鍵的函數(shù)getTable,后面會對其進(jìn)行具體分析,先記下。

get操作:

public V get(Object key) {
        // 確定key
        Object k = maskNull(key);
        // 計算其hashCode
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int index = indexFor(h, tab.length);
        // 獲取對應(yīng)位置上的元素
        Entry<K,V> e = tab[index];
        while (e != null) {
            // 如果hashCode相同,并且key也相同,則返回,否則繼續(xù)循環(huán)
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        // 未找到,則返回null
        return null;
    }

分析:

get操作邏輯簡單,根據(jù)key遍歷對應(yīng)元素即可。

remove操作:

public V remove(Object key) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);
        // 數(shù)組上第一個元素
        Entry<K,V> prev = tab[i];
        Entry<K,V> e = prev;
        // 循環(huán)
        while (e != null) {
            Entry<K,V> next = e.next;
            // 如果hash值相同,并且key一樣,則進(jìn)行移除操作
            if (h == e.hash && eq(k, e.get())) {
                // 修改次數(shù)自增
                modCount++;
                // 元素個數(shù)自減
                size--;
                // 如果就是頭元素,則直接移除即可
                if (prev == e)
                    tab[i] = next;
                else
                    // 否則將前驅(qū)元素的next賦值為next,則將e移除
                    prev.next = next;
                return e.value;
            }
            // 更新prev和e,繼續(xù)循環(huán)
            prev = e;
            e = next;
        }
        return null;
    }

分析:

移除元素操作的整體邏輯并不復(fù)雜,就是進(jìn)行鏈表的常規(guī)操作,注意元素是鏈表頭時的特別處理,通過上述注釋,理解應(yīng)該不困難。

resize操作(WeakHashMap的擴(kuò)容操作)

void resize(int newCapacity) {
    Entry<K,V>[] oldTable = getTable();
    // 原數(shù)組長度
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    // 創(chuàng)建新的數(shù)組
    Entry<K,V>[] newTable = newTable(newCapacity);
    // 數(shù)據(jù)轉(zhuǎn)移
    transfer(oldTable, newTable);
    table = newTable;
    /*
     * If ignoring null elements and processing ref queue caused massive
     * shrinkage, then restore old table.  This should be rare, but avoids
     * unbounded expansion of garbage-filled tables.
     */
    // 確定擴(kuò)容閾值
    if (size >= threshold / 2) {
        threshold = (int)(newCapacity * loadFactor);
    } else {
        // 清除被GC的value
        expungeStaleEntries();
        // 數(shù)組轉(zhuǎn)移
        transfer(newTable, oldTable);
        table = oldTable;
    }
}
 private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
    // 遍歷原數(shù)組
    for (int j = 0; j < src.length; ++j) {
        // 取出元素
        Entry<K,V> e = src[j];
        src[j] = null;
        // 鏈?zhǔn)秸以?
        while (e != null) {
            Entry<K,V> next = e.next;
            Object key = e.get();
            // key被回收的情況
            if (key == null) {
                e.next = null;  // Help GC
                e.value = null; //  "   "
                size--;
            } else {
                // 確定在新數(shù)組的位置
                int i = indexFor(e.hash, dest.length);
                // 插入元素 注意這里為頭插法,會倒序
                e.next = dest[i];
                dest[i] = e;
            }
            e = next;
        }
    }
}

分析:

WeakHashMap的擴(kuò)容函數(shù)中有點(diǎn)特別,因?yàn)閗ey可能被GC掉,所以在擴(kuò)容時也許要考慮這種情況,其他并沒有什么特別的,通過以上注釋理解應(yīng)該不難。

在以上源碼分析中多次出現(xiàn)一個函數(shù):expungeStaleEntries

private void expungeStaleEntries() {
        // 從隊(duì)列中取出被GC的Entry
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                // 確定元素在隊(duì)列中的位置
                int i = indexFor(e.hash, table.length);
                // 取出數(shù)組中的第一個元素 prev
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                // 循環(huán)
                while (p != null) {
                    Entry<K,V> next = p.next;
                    // 找到
                    if (p == e) {
                        // 判斷是否是鏈表頭元素 第一次時
                        if (prev == e)
                            // 將next直接掛在i位置
                            table[i] = next;
                        else
                            // 進(jìn)行截斷
                            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
                    prev = p;
                    p = next;
                }
            }
        }
    }

分析:

該函數(shù)的主要作用就是清除Entry的value,該Entry是在GC清除key的過程中入隊(duì)的。函數(shù)的邏輯并不復(fù)雜,通過上述注釋理解應(yīng)該不難。

接下來看下該函數(shù)會在什么時候調(diào)用:

從以上調(diào)用鏈可知,在獲取size(獲取WeakHashMap的元素個數(shù))和resize(擴(kuò)容時)會調(diào)用該函數(shù)清除被GC的key對應(yīng)的value值。但還有一個getTable函數(shù)也會調(diào)用該函數(shù):

從以上調(diào)用鏈可知,在get/put等操作中都會調(diào)用expungeStaleEntries函數(shù)進(jìn)GC后的收尾工作,其實(shí)WeakHashMap清除無強(qiáng)引用的核心也就是該函數(shù)了,因此理解該函數(shù)的作用是非常重要的。

3.總結(jié)

1.WeakHashMap非同步,默認(rèn)容量為16,擴(kuò)容因子默認(rèn)為0.75,底層數(shù)據(jù)結(jié)構(gòu)為Entry數(shù)組(數(shù)組+鏈表)。

2.WeakHashMap中的弱引用key會在下一次GC被清除,注意只會清除key,value會在每次map操作中清除。

3.在WeakHashMap中強(qiáng)引用的key是不會被GC清除的。

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

相關(guān)文章

  • 關(guān)于mybatis-plus-generator的簡單使用示例詳解

    關(guān)于mybatis-plus-generator的簡單使用示例詳解

    在springboot項(xiàng)目中集成mybatis-plus是很方便開發(fā)的,最近看了一下plus的文檔,簡單用一下它的代碼生成器,接下來通過實(shí)例代碼講解關(guān)于mybatis-plus-generator的簡單使用,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • Java面試題沖刺第三天--集合框架篇

    Java面試題沖刺第三天--集合框架篇

    這篇文章主要為大家分享了最有價值的三道java面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-07-07
  • java開發(fā)之Jdbc分頁源碼詳解

    java開發(fā)之Jdbc分頁源碼詳解

    這篇文章主要介紹了java開發(fā)之Jdb分頁源碼詳解,需要的朋友可以參考下
    2020-02-02
  • Spring Boot中防止遞歸查詢的兩種方式

    Spring Boot中防止遞歸查詢的兩種方式

    這篇文章主要給大家介紹了關(guān)于Spring Boot中防止遞歸查詢的兩種方式,兩種方式分別是在application.properties中配置和在entity中添加注解,都給出了詳細(xì)的示例代碼,需要的朋友們下面來一起看看吧。
    2017-06-06
  • 基于java Springboot實(shí)現(xiàn)教務(wù)管理系統(tǒng)詳解

    基于java Springboot實(shí)現(xiàn)教務(wù)管理系統(tǒng)詳解

    這篇文章主要介紹了Java 實(shí)現(xiàn)簡易教務(wù)管理系統(tǒng)的代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • 這一次搞懂Spring的XML解析原理說明

    這一次搞懂Spring的XML解析原理說明

    這篇文章主要介紹了這一次搞懂Spring的XML解析原理說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • spring Retryable注解實(shí)現(xiàn)重試詳解

    spring Retryable注解實(shí)現(xiàn)重試詳解

    這篇文章主要介紹了spring Retryable注解實(shí)現(xiàn)重試詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java實(shí)現(xiàn)更新順序表中的指定元素的示例

    Java實(shí)現(xiàn)更新順序表中的指定元素的示例

    本文主要介紹了Java實(shí)現(xiàn)更新順序表中的指定元素的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • MyBatis使用Map與模糊查詢的方法示例

    MyBatis使用Map與模糊查詢的方法示例

    這篇文章主要給大家介紹了關(guān)于MyBatis使用Map與模糊查詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Java使用正則獲取括號里面的內(nèi)容

    Java使用正則獲取括號里面的內(nèi)容

    這篇文章主要介紹了Java使用正則獲取括號里面的內(nèi)容問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05

最新評論