Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解
1 WeakHashMap 的原理
基于哈希表的Map接口實(shí)現(xiàn),支持null鍵和值,但是WeakHashMap具有弱鍵,可用來實(shí)現(xiàn)緩存存儲,在進(jìn)行GC的時候會自動回收鍵值對。
WeakHashMap 的 Entry 節(jié)點(diǎn)繼承自 WeakReference。put方法插入鍵值對時,創(chuàng)建Entry節(jié)點(diǎn)時,key被WeakReference引用,get方法獲取key的時候,實(shí)際上是從WeakReference中獲取的。
Value正常引用存儲,每次創(chuàng)建插入Entry 節(jié)點(diǎn)的時候,還會給WeakReference對象關(guān)聯(lián)一個引用隊(duì)列ReferenceQueue。
/**
* 已清除的 WeakEntries 的引用隊(duì)列
*/
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
/**
* Entry繼承了WeakReference,key被WeakReference直接關(guān)聯(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引用的對象在沒有其他外部引用關(guān)聯(lián)時,在下一次垃圾回收時將會回收該對象,并且其關(guān)聯(lián)的WeakReference對象也會被加入到相關(guān)的引用隊(duì)列中。
如果某個key因?yàn)闆]有其他外部引用被“回收”了getKey()方法就獲取不到key了,就會返回null,其對應(yīng)的Entry也會被加入到相關(guān)的引用隊(duì)列中去了,此時這個Entry也就訪問不到了,看起來整個Entry就像被回收了一樣,但是此時這個Entry并沒有被回收,因?yàn)樗€被內(nèi)部table數(shù)組引用了。
**無效的Entry怎么被清除呢? **實(shí)際上當(dāng)我們每次需要操作WeakHashMap時,會先清除無效的Entry,位于 expungeStaleEntries 方法中。table中保存了全部的Entry鍵值對,而queue中保存被GC回收的Entry鍵值對,通過比對就能刪除table中被GC回收的Entry鍵值對,這樣就能清除無效的Entry了。
2 tomcat的ConcurrentCache
Tomcat的ConcurrentCache就使用了 WeakHashMap 來實(shí)現(xiàn)緩存功能。
ConcurrentCache 采取的是分代緩存,其內(nèi)部保存了兩個Map:
- 經(jīng)常使用的對象放入 eden 中,eden 使用 ConcurrentHashMap 實(shí)現(xiàn),不用擔(dān)心會被回收(伊甸園);
- 不常用的對象放入 longterm,longterm 使用 WeakHashMap 實(shí)現(xiàn),這些不常使用的對象會被垃圾收集器回收。
- 當(dāng)調(diào)用 get() 方法時,會先從 eden 區(qū)獲取,如果沒有找到的話再到 longterm 獲取,當(dāng)從 longterm 獲取到就把對象放入 eden 中,從而保證經(jīng)常被訪問的節(jié)點(diǎn)不容易被回收。
- 當(dāng)調(diào)用 put() 方法時,如果 eden 的大小超過了 size,那么就將 eden 中的所有對象都放入 longterm 中,利用虛擬機(jī)回收掉一部分不經(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);
}
}到此這篇關(guān)于Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解的文章就介紹到這了,更多相關(guān)WeakHashMap及ConcurrentCache原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring超詳細(xì)講解事務(wù)和事務(wù)傳播機(jī)制
Spring事務(wù)的本質(zhì)就是對數(shù)據(jù)庫事務(wù)的支持,沒有數(shù)據(jù)庫事務(wù),Spring是無法提供事務(wù)功能的。Spring只提供統(tǒng)一的事務(wù)管理接口,具體實(shí)現(xiàn)都是由數(shù)據(jù)庫自己實(shí)現(xiàn)的,Spring會在事務(wù)開始時,根據(jù)當(dāng)前設(shè)置的隔離級別,調(diào)整數(shù)據(jù)庫的隔離級別,由此保持一致2022-06-06
SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級數(shù)據(jù)的具體實(shí)現(xiàn)
ThreadPoolTaskExecutor是Spring提供的任務(wù)執(zhí)行器實(shí)現(xiàn)之一,允許開發(fā)者配置線程池參數(shù)以適應(yīng)不同的應(yīng)用場景,創(chuàng)建 ThreadPoolTaskExecutor 實(shí)例并設(shè)置核心和最大線程數(shù)等屬性可以優(yōu)化性能,本文介紹了SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級數(shù)據(jù)的具體實(shí)現(xiàn)2024-12-12
Java中雙重檢查鎖(double checked locking)的正確實(shí)現(xiàn)
雙重檢查鎖(Double-Check Locking),顧名思義,通過兩次檢查,并基于加鎖機(jī)制,實(shí)現(xiàn)某個功能,下面這篇文章主要給大家介紹了關(guān)于Java中雙重檢查鎖(double checked locking)的相關(guān)資料,需要的朋友可以參考下2021-09-09
Java使用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請求的示例代碼
CountDownLatch 是一個同步工具類,用來協(xié)調(diào)多個線程之間的同步,它能夠使一個線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。被將利用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請求,異步同時獲取商品信息組裝,感興趣的可以了解一下2023-01-01
mybatis-plus踩坑之下劃線駝峰轉(zhuǎn)換方式
MyBatis-Plus默認(rèn)駝峰轉(zhuǎn)下劃線導(dǎo)致實(shí)體字段匹配失敗,關(guān)閉該功能后需手動配置映射,建議避免字段使用下劃線,若必須則可通過注解、重寫toString或調(diào)整字段名解決2025-08-08
教你用Java Swing實(shí)現(xiàn)自助取款機(jī)系統(tǒng)
今天給大家?guī)淼氖顷P(guān)于JAVA的相關(guān)知識,文章圍繞著如何用Java Swing實(shí)現(xiàn)自助取款機(jī)系統(tǒng)展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Netty分布式ByteBuf使用的底層實(shí)現(xiàn)方式源碼解析
這篇文章主要為大家介紹了Netty分布式ByteBuf使用底層實(shí)現(xiàn)方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03

