Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解
1 WeakHashMap 的原理
基于哈希表的Map接口實現(xiàn),支持null鍵和值,但是WeakHashMap具有弱鍵,可用來實現(xiàn)緩存存儲,在進行GC的時候會自動回收鍵值對。
WeakHashMap 的 Entry 節(jié)點繼承自 WeakReference。put方法插入鍵值對時,創(chuàng)建Entry節(jié)點時,key被WeakReference引用,get方法獲取key的時候,實際上是從WeakReference中獲取的。
Value正常引用存儲,每次創(chuàng)建插入Entry 節(jié)點的時候,還會給WeakReference對象關聯(lián)一個引用隊列ReferenceQueue。
/** * 已清除的 WeakEntries 的引用隊列 */ private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); /** * Entry繼承了WeakReference,key被WeakReference直接關聯(lián) */ 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; } @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; } //………… }
key被WeakReference對象引用,它就是一個弱鍵。根據(jù)Java弱引用的特性,被WeakReference引用的對象在沒有其他外部引用關聯(lián)時,在下一次垃圾回收時將會回收該對象,并且其關聯(lián)的WeakReference對象也會被加入到相關的引用隊列中。
如果某個key因為沒有其他外部引用被“回收”了getKey()方法就獲取不到key了,就會返回null,其對應的Entry也會被加入到相關的引用隊列中去了,此時這個Entry也就訪問不到了,看起來整個Entry就像被回收了一樣,但是此時這個Entry并沒有被回收,因為它還被內(nèi)部table數(shù)組引用了。
**無效的Entry怎么被清除呢? **實際上當我們每次需要操作WeakHashMap時,會先清除無效的Entry,位于 expungeStaleEntries 方法中。table中保存了全部的Entry鍵值對,而queue中保存被GC回收的Entry鍵值對,通過比對就能刪除table中被GC回收的Entry鍵值對,這樣就能清除無效的Entry了。
2 tomcat的ConcurrentCache
Tomcat的ConcurrentCache就使用了 WeakHashMap 來實現(xiàn)緩存功能。
ConcurrentCache 采取的是分代緩存,其內(nèi)部保存了兩個Map:
- 經(jīng)常使用的對象放入 eden 中,eden 使用 ConcurrentHashMap 實現(xiàn),不用擔心會被回收(伊甸園);
- 不常用的對象放入 longterm,longterm 使用 WeakHashMap 實現(xiàn),這些不常使用的對象會被垃圾收集器回收。
- 當調(diào)用 get() 方法時,會先從 eden 區(qū)獲取,如果沒有找到的話再到 longterm 獲取,當從 longterm 獲取到就把對象放入 eden 中,從而保證經(jīng)常被訪問的節(jié)點不容易被回收。
- 當調(diào)用 put() 方法時,如果 eden 的大小超過了 size,那么就將 eden 中的所有對象都放入 longterm 中,利用虛擬機回收掉一部分不經(jīng)常使用的對象。
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); } }
到此這篇關于Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解的文章就介紹到這了,更多相關WeakHashMap及ConcurrentCache原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot中RestTemplate配置HttpClient連接池詳解
這篇文章主要介紹了springboot中RestTemplate配置HttpClient連接池詳解,這些Http連接工具,使用起來都比較復雜,如果項目中使用的是Spring框架,可以使用Spring自帶的RestTemplate來進行Http連接請求,需要的朋友可以參考下2023-11-11Spring boot 應用實現(xiàn)動態(tài)刷新配置詳解
這篇文章主要介紹了spring boot 配置動態(tài)刷新實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2021-09-09Java的非阻塞隊列ConcurrentLinkedQueue解讀
這篇文章主要介紹了Java的非阻塞隊列ConcurrentLinkedQueue解讀,在并發(fā)編程中,有時候需要使用線程安全的隊列,如果要實現(xiàn)一個線程安全的隊列有兩種方式:一種是使用阻塞算法,另一種是使用非阻塞算法,需要的朋友可以參考下2023-12-12Java使用pdfbox實現(xiàn)給pdf文件加圖片水印
有時候需要給pdf加水印,市面上工具都是收費的要會員,還是自食其力吧;嘗試過 spire.pdf.free 那個超過10頁就不行了!所以本文還是使用了pdfbox,感興趣的可以了解一下2022-11-11