Java弱引用集合WeakHashMap總結(jié)
前言
WeakHashMap利用WeakReference的弱引用特性讓用戶(hù)在使用的過(guò)程中不會(huì)因?yàn)闆](méi)有釋放Map中的資源而導(dǎo)致內(nèi)存泄露。
WeakHashMap實(shí)現(xiàn)了Map接口,使用方式和其他的Map相同,需要注意的是get方法和size方法的使用。在介紹WeakHashMap之前需要先介紹一下Reference的概念。
1、Reference的實(shí)現(xiàn)
public abstract class Reference<T> {
private T referent; //對(duì)應(yīng)的引用對(duì)象
volatile ReferenceQueue<? super T> queue; //引用隊(duì)列,初始化的時(shí)候從外部傳入
Reference next; // 當(dāng)引用對(duì)象可達(dá)性發(fā)生變化的時(shí)候,next會(huì)指向當(dāng)前引用
transient private Reference<T> discovered; /* used by VM */
static private class Lock { }
private static Lock lock = new Lock(); //用來(lái)同步鎖操作
}Reference類(lèi)有四個(gè)直接子類(lèi),PhantomReference、FinalReference、SoftReference、WeakReference。
其中SoftReference比WeakReference約束要強(qiáng)一些,當(dāng)內(nèi)存不夠用的時(shí)候JVM才會(huì)將對(duì)應(yīng)引用的對(duì)象刪除掉,而WeakReference在對(duì)象引用不可達(dá)的時(shí)候就會(huì)被JVM清理掉,PhantomReference(虛引用)和約束更弱,get方法永遠(yuǎn)都返回null,無(wú)法像前兩者一樣可以通過(guò)get()方法獲取一個(gè)強(qiáng)引用,PhantomReference只能用來(lái)監(jiān)控對(duì)象的GC狀況。
無(wú)論哪種Reference,都有一個(gè)重要的對(duì)象來(lái)跟蹤對(duì)象的gc動(dòng)作,這個(gè)就是ReferenceQueue。
2、ReferenceQueue的實(shí)現(xiàn)
public class ReferenceQueue {
static private class Lock { }; //鎖對(duì)象,用來(lái)同步隊(duì)列操作
private Lock lock = new Lock();
private volatile Reference<? extends T> head = null; //頭結(jié)點(diǎn)
private long queueLength = 0; //隊(duì)列長(zhǎng)度
}入隊(duì)操作
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
synchronized (lock) {
ReferenceQueue<?> queue = r.queue; //獲取reference之前的隊(duì)列,如果沒(méi)有綁定隊(duì)列,那就不需要入隊(duì)
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
r.queue = ENQUEUED;
r.next = (head == null) ? r : head; //把r從鏈表的頭部插入
head = r; //頭結(jié)點(diǎn)指向r
queueLength++; //隊(duì)列長(zhǎng)度+1
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll(); //喚醒刪除線程刪除頭結(jié)點(diǎn)
return true;
}
}出隊(duì)列的操作相似,每個(gè)reference就是鏈表中的一個(gè)節(jié)點(diǎn),next指向下一個(gè)reference節(jié)點(diǎn)。
刪除操作
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException
{
if (timeout < 0) {
throw new IllegalArgumentException("Negative timeout value");
}
synchronized (lock) {
Reference<? extends T> r = reallyPoll(); //嘗試獲取隊(duì)列的頭結(jié)點(diǎn)
if (r != null) return r; //獲取成功,返回
long start = (timeout == 0) ? 0 : System.nanoTime(); //如果超時(shí)時(shí)間不為0,就記錄一下當(dāng)前的開(kāi)始時(shí)間
for (;;) {
lock.wait(timeout);
r = reallyPoll();
if (r != null) return r;
if (timeout != 0) {
long end = System.nanoTime();
timeout -= (end - start) / 1000_000; //計(jì)算一下等待的時(shí)間
if (timeout <= 0) return null; //確認(rèn)等待是否是超時(shí),如果是就返回,否則就認(rèn)為是有入隊(duì)請(qǐng)求喚醒當(dāng)前線程,但是當(dāng)前線程嘗試刪除頭結(jié)點(diǎn)失敗了(被其他線程刪除了),那么繼續(xù)嘗試刪除頭結(jié)點(diǎn),再次執(zhí)行循環(huán)中的內(nèi)容直到超時(shí)
start = end;
}
}
}
}ReferenceQueue是線程安全的,出隊(duì)入隊(duì)操作都由lock對(duì)象來(lái)保證線程安全,當(dāng)用戶(hù)線程和jvm線程同時(shí)訪問(wèn)ReferenceQueue的時(shí)候不會(huì)出現(xiàn)并發(fā)問(wèn)題。
WeakHashMap
WeakHashMap內(nèi)部同樣是通過(guò)一個(gè)數(shù)組來(lái)實(shí)現(xiàn)存儲(chǔ),解決沖突的方式也是使用拉鏈法,weakHashMap中重新定義了Entry類(lèi)來(lái)存儲(chǔ)kv鍵值對(duì),Entry的實(shí)現(xiàn)也是實(shí)現(xiàn)WeakHashMap特性的關(guān)鍵。
1、Entry的定義
WeakHashMap重新定義了一個(gè)entry,這個(gè)entry繼承了WeakReference類(lèi)并且實(shí)現(xiàn)了Entry接口,使用該Entry存儲(chǔ)鍵值對(duì)不會(huì)產(chǎn)生強(qiáng)引用
jvm在垃圾回收的時(shí)候不會(huì)認(rèn)為該引用是強(qiáng)引用,會(huì)正常的回收對(duì)象
Entry的定義如下
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value; //存儲(chǔ)的value
final int hash; //hash值
Entry<K,V> next; //拉鏈法解決沖突,形成單鏈表
Entry(Object key, V value,ReferenceQueue<Object> queue, int hash, Entry<K,V> next) {
super(key, queue); //這里的引用對(duì)象是key,跟蹤的是key對(duì)象的垃圾回收
this.value = value;
this.hash = hash;
this.next = next;
}
...
}Entry的構(gòu)造器需要傳入ReferenceQueue,這個(gè)queue就可以用來(lái)監(jiān)控全局的Entry被清理的情況。
2、清理操作
當(dāng)對(duì)象被垃圾回收的時(shí)候,當(dāng)前Map需要?jiǎng)h除掉對(duì)應(yīng)的Entry,因?yàn)镋ntry此時(shí)指向的對(duì)象已經(jīng)被回收,所以需要找到被JVM回收的對(duì)象對(duì)應(yīng)的Entry并且將Entry對(duì)象從Map中移除掉,實(shí)現(xiàn)方式是expungeStaleEntries方法
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); //從隊(duì)列中拿到entry,根據(jù)entry獲取entry鏈表在table中的索引
Entry<K,V> prev = table[i]; //找到entry鏈表的頭結(jié)點(diǎn)
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next; //找到對(duì)應(yīng)節(jié)點(diǎn)
else
prev.next = next;
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}該方法在map中的getTable()、size()和resize()方法中被調(diào)用,每次put或是get的時(shí)候都會(huì)先執(zhí)行g(shù)etTable()方法,因此每次讀寫(xiě)數(shù)據(jù)的時(shí)候都會(huì)清理掉無(wú)用的entry,所以用戶(hù)不會(huì)獲取到被垃圾回收的清理entry
因此,每次用戶(hù)調(diào)用get方法或是size方法的時(shí)候,都會(huì)觸發(fā)清理操作,所以每次返回的結(jié)果可能都不相同,因?yàn)閮?nèi)部的entry持有的對(duì)象已經(jīng)被jvm回收。
線程安全問(wèn)題
WeakHashMap作為容器本事不是線程安全的,但是在使用過(guò)程中,ReferenceQueue可能會(huì)被當(dāng)前業(yè)務(wù)線程和JVM線程并發(fā)訪問(wèn),ReferenceQueue是線程安全的。
到此這篇關(guān)于Java弱引用集合WeakHashMap總結(jié)的文章就介紹到這了,更多相關(guān)WeakHashMap總結(jié)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis中使用#{}和${}占位符傳遞參數(shù)的各種報(bào)錯(cuò)信息處理方案
這篇文章主要介紹了MyBatis中使用#{}和${}占位符傳遞參數(shù)的各種報(bào)錯(cuò)信息處理方案,分別介紹了兩種占位符的區(qū)別,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
JavaScript Date時(shí)間對(duì)象的常用操作方法總結(jié)
本文介紹了JavaScript中Date對(duì)象的常用操作方法,包括創(chuàng)建、獲取、設(shè)置本地及UTC時(shí)間等,幫助開(kāi)發(fā)者靈活處理日期與時(shí)間的相關(guān)需求,需要的朋友可以參考下2025-10-10
詳解idea maven nexus 常見(jiàn)命令配置
這篇文章主要介紹了idea maven nexus 常見(jiàn)命令配置的相關(guān)知識(shí),通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
Jenkins環(huán)境搭建之下載與安裝過(guò)程
Jenkins是一個(gè)功能強(qiáng)大的應(yīng)用程序,允許持續(xù)集成和持續(xù)交付項(xiàng)目,集成Jenkins可以用于一些測(cè)試和部署技術(shù),對(duì)Jenkins環(huán)境搭建之下載與安裝過(guò)程感興趣的朋友跟隨小編一起看看吧2021-12-12
關(guān)于ThreadLocal對(duì)request和response的用法說(shuō)明
這篇文章主要介紹了關(guān)于ThreadLocal對(duì)request和response的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
簡(jiǎn)單總結(jié)SpringMVC攔截器的使用方法
今天給大家?guī)?lái)的是關(guān)于SpringMVC攔截器的相關(guān)知識(shí),文章圍繞著SpringMVC攔截器的使用方法展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
詳解Java的MyBatis框架和Spring框架的整合運(yùn)用
在Web端的SSH框架整合中Spring主要負(fù)責(zé)數(shù)據(jù)庫(kù)處理,而引入MyBatis后二者的集成使用效果更佳,下面我們就來(lái)詳解Java的MyBatis框架和Spring框架的整合運(yùn)用2016-06-06
idea左側(cè)的commit框設(shè)置顯示出來(lái)方式
在IDEA中顯示左側(cè)的commit框,首先通過(guò)File-Settings-Version Control-Commit進(jìn)行設(shè)置,然后勾選Use non-modal commit interface完成2025-01-01

