您好,登錄后才能下訂單哦!
小編給大家分享一下Java中弱引用指的是什么意思,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
弱引用主要應(yīng)用在不阻止它的key或者value 被回收的mapping。弱引用的出現(xiàn)就是為了垃圾回收服務(wù)的。它引用一個(gè)對(duì)象,但是并不阻止該對(duì)象被回收。如果使用一個(gè)強(qiáng)引用的話,只要該引用存在,那么被引用的對(duì)象是不能被回收的。弱引用則沒有這個(gè)問題。在垃圾回收器運(yùn)行的時(shí)候,如果一個(gè)對(duì)象的所有引用都是弱引用的話,該對(duì)象會(huì)被回收。
弱引用案例深度解析
理想的情況下,我們希望當(dāng)我們不再使用一個(gè)對(duì)象的時(shí)候,能夠在gc 發(fā)生的時(shí)候就把它回收掉。但是有些時(shí)候,由于我們的粗忽,在壞的情況下會(huì)導(dǎo)致內(nèi)存溢出。這種案例尤其發(fā)生在一個(gè)生命使用周期很長(zhǎng)的map 存放了很多實(shí)際使用生命周期短的對(duì)象。請(qǐng)看下面這個(gè)例子:
public class StrongRefenceDemo { static Map map; public static void main(String[] args) throws Exception { StrongRefenceDemo demo = new StrongRefenceDemo(); demo.strongTest(); System.out.println("gc 發(fā)生前:" + map.size()); System.out.println("開始通知GC"); //注意,這里只是通過垃圾回收器進(jìn)行垃圾回收,并不一定馬上執(zhí)行 System.gc(); Thread.sleep(1000 * 5); System.out.println("gc 發(fā)生后:" + map.size()); } /** * 強(qiáng)引用測(cè)試 */ public void strongTest() { map = new HashMap<>(); String key = new String("key"); String value = new String("value"); map.put(key, value); key = null; value = null; } }
運(yùn)行后輸出結(jié)果:
gc 發(fā)生前:1 開始通知GC gc 發(fā)生后:1
從輸出的結(jié)果可以看到,即使我們通過把key和value 設(shè)置為null 來告訴jvm,我們不再使用這個(gè)對(duì)象了,map 里面對(duì)象依然沒有被GC 回收,因?yàn)閗ey和value 被一個(gè)強(qiáng)引用map 指向,根據(jù)可達(dá)性判斷,垃圾回收器是不能回收掉key和value 這個(gè)對(duì)象的。map 被定義為statis 的靜態(tài)變量,是一個(gè)使用生命周期很長(zhǎng)的對(duì)象。在strongTest()方法中存在了一個(gè)key和value 的局部變量,它隨著方法的執(zhí)行完,這個(gè)變量的生命使用周期就結(jié)束了,但是粗糙的程序員忘記remove 了,這個(gè)時(shí)候垃圾回收器是不能回收它的。如果這種生命周期相對(duì)短的對(duì)象很多,最終就有可能消耗掉JVM中全部的內(nèi)存。
但是這里我有一個(gè)好奇,假如這里的key和value 指向的對(duì)象在執(zhí)行完strongTest()方法 以后用不著了,但是我可能又不是很好的判斷去主動(dòng)調(diào)用remove 來移除它。想要垃圾回收器自己判斷回收掉可不可以呢?答案其實(shí)是可以的,這個(gè)時(shí)候就是弱引用上場(chǎng)了,請(qǐng)看下面程序
public class WeakRefenceDemo { static Map, String> map; public static void main(String[] args) throws Exception { WeakRefenceDemo demo = new WeakRefenceDemo(); demo.weakTest(); System.out.println("gc 發(fā)生前:" + map.size()); System.out.println("開始通知GC"); //注意,這里只是通過垃圾回收器進(jìn)行垃圾回收,并不一定馬上執(zhí)行 System.gc(); Thread.sleep(1000 * 5); System.out.println("gc 發(fā)生后:" + map.size()); } /** * 若引用測(cè)試 */ public void weakTest() { map = new WeakHashMap<>(); String key = new String("key"); String value = new String("value"); map.put(new WeakReference<>(key), value); key = null; value = null; } }
運(yùn)行上面代碼輸出結(jié)果
gc 發(fā)生前:1 開始通知GC gc 發(fā)生后:0
從輸出結(jié)果0,我們可以判斷已經(jīng)成功被垃圾回收了。what?整個(gè)過程我們只是把HashMap 換成了WeakHashMap,并且key 由String 換成了WeakReference。其實(shí)就是由于字符串只有弱引用指向,所以可以被垃圾回收掉。是不是很簡(jiǎn)單,如果到這里你就停止研究弱引用了,那就太暴殄天物了
WeakHashMap 深度解析
上面的程序片段中,其實(shí)只有key 設(shè)置了為弱引用new WeakReference<>(key),那正常也就只有這個(gè)key 對(duì)應(yīng)的內(nèi)存被回收而已,由于沒有調(diào)用remove ,里面的value 和entry 也是不會(huì)回收掉的,那為什么最后輸出的size 是0 呢? 很好的問題,我們深入去看WeakHashMap 的源碼,我們發(fā)現(xiàn)了一個(gè)神奇的方法expungeStaleEntries()。在看源碼之前先解析下引用隊(duì)列的概念: 在弱引用被回收的時(shí)候會(huì)把該對(duì)象放到引用隊(duì)列中,也就意味著從引用隊(duì)列中獲取的對(duì)象都是被回收的對(duì)象,先解釋到這里,足以滿足我們下面的源碼分析了,接下來會(huì)做詳細(xì)的解析
/** * Expunges stale entries from the table. */ private void expungeStaleEntries() { //這里從引用隊(duì)列里面取出一個(gè)已經(jīng)被回收的對(duì)象 for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry e = (Entry) x; int i = indexFor(e.hash, table.length); Entry prev = table[i]; Entry p = prev; //下面就是通過遍歷鏈表來設(shè)置值為null 來告訴垃圾回收器回收掉 //注意WeakHashMap 和HashMap 的數(shù)據(jù)結(jié)構(gòu)都是通過數(shù)組+鏈表組成的,只有理解了這點(diǎn)才知道下面的代碼做了什么 while (p != null) { Entry next = p.next; //相等的時(shí)候,就意味著這個(gè)就是要回收的對(duì)象 if (p == e) { //下面就是讓回收對(duì)象不再被引用 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 //這里通過設(shè)置value 為null 來告訴垃圾回收 e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }
從上面的代碼片段,大概的意思就是從引用隊(duì)列里面取出被回收的對(duì)象,然后和WeakHashMap 中的對(duì)象查找,找到之后就把對(duì)應(yīng)的value 也設(shè)置為null,并且把對(duì)應(yīng)的entry 設(shè)置為null,來告訴GC 去回收它。從源碼可以看到expungeStaleEntries() 這個(gè)方法在執(zhí)行WeakHashMap中的任何方法的時(shí)候都會(huì)被調(diào)用到的
/** * Expunges stale entries from the table. */ private void expungeStaleEntries() { //這里從引用隊(duì)列里面取出一個(gè)已經(jīng)被回收的對(duì)象 for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry e = (Entry) x; int i = indexFor(e.hash, table.length); Entry prev = table[i]; Entry p = prev; //下面就是通過遍歷鏈表來設(shè)置值為null 來告訴垃圾回收器回收掉 //注意WeakHashMap 和HashMap 的數(shù)據(jù)結(jié)構(gòu)都是通過數(shù)組+鏈表組成的,只有理解了這點(diǎn)才知道下面的代碼做了什么 while (p != null) { Entry next = p.next; //相等的時(shí)候,就意味著這個(gè)就是要回收的對(duì)象 if (p == e) { //下面就是讓回收對(duì)象不再被引用 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 //這里通過設(shè)置value 為null 來告訴垃圾回收 e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }
到這里也就完全明白為什么value 不設(shè)置為弱引用和沒有顯性的調(diào)用remove 方法也可以回收掉了。
引用隊(duì)列
從上面的的源碼中,我們大概知道了引用隊(duì)列的使用,那為什么要使用引用隊(duì)列呢?假如沒有引用隊(duì)列,上面的例子我們就需要遍歷全部的元素一個(gè)一個(gè)的去找,如果數(shù)量少那還好,如果數(shù)量多的時(shí)候,肯定就會(huì)出現(xiàn)一些性能問題。有了引用隊(duì)列那就輕松可以解決上面的問題了。從WeakReference 源碼中我們可以看到有兩個(gè)構(gòu)造函數(shù),第二個(gè)是需要傳入引用隊(duì)列的
public WeakReference(T referent) { super(referent); } public WeakReference(T referent, ReferenceQueue q) { super(referent, q); }
總結(jié)
弱引用的出現(xiàn)是為了垃圾回收的
一個(gè)對(duì)象只有弱引用指向它的時(shí)候,它是可以被回收的
弱引用是在GC 發(fā)生的時(shí)候就進(jìn)行回收,不管當(dāng)時(shí)內(nèi)存是否充足
如果你在創(chuàng)建弱引用指定一個(gè)引用隊(duì)列的話,弱引用對(duì)象被回收的時(shí)候,會(huì)把該對(duì)象放入引用隊(duì)列中
為了安全使用,每次都要判斷下是否為空來判斷該對(duì)象是否已經(jīng)被回收,來避免空指針異常
以上是“Java中弱引用指的是什么意思”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。