詳解Java中的四種引用類型(強(qiáng)軟弱虛)
強(qiáng)引用(StrongReference)
強(qiáng)引用指的就是代碼中普遍存在的賦值方式,比如A a = new A()
這種。強(qiáng)引用關(guān)聯(lián)的對(duì)象,永遠(yuǎn)不會(huì)被GC回收。
當(dāng)內(nèi)存空間不足的時(shí)候,java虛擬機(jī)寧愿拋出OutOfMemoryError
錯(cuò)誤,是程序異常終止,也不會(huì)回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題
obj=null //幫助垃圾回收器回收這個(gè)對(duì)象
顯式地設(shè)置obj為null,或者說(shuō)超出對(duì)象的生命周期范圍,則GC認(rèn)為該對(duì)象不存在引用,這時(shí)候就可以回收這個(gè)對(duì)象了,具體什么時(shí)候回收這要看GC的算法是怎么樣的。
當(dāng)一個(gè)方法的內(nèi)部有一個(gè)強(qiáng)引用,這個(gè)引用保存在棧中,而這個(gè)引用的內(nèi)容存放在堆中,當(dāng)這個(gè)方法運(yùn)行完成后就會(huì)退出方法棧,那么引用內(nèi)容也會(huì)跟著就會(huì)不存在了,這個(gè)Object就會(huì)被回收。但是如果個(gè)這個(gè)obj是一個(gè)全局變量的時(shí)候,就需要再不用的時(shí)候?qū)①x值為null,因?yàn)閺?qiáng)引用是不會(huì)被垃圾回收的
在ArrayList的clear方法中就用到了強(qiáng)引用
private transient Object[] elementData; public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) { elementData[i] = null; } size = 0; }
在 ArrayList 類中定義了一個(gè)私有的變量 elementData 數(shù)組,在調(diào)用方法清空數(shù)組時(shí)可以看到為每個(gè)數(shù)組內(nèi)容賦值為null。 不同于elementData = null,強(qiáng)引用仍然存在,避免在后續(xù)調(diào)用 add()等方法添加元素時(shí),進(jìn)行重新的內(nèi)存分配。使用如 clear() 方法中釋放內(nèi)存的方法對(duì)數(shù)組中存放的引用類型特別適用,這樣就可以及時(shí)釋放內(nèi)存。
軟引用(SoftReference)
軟引用可以用SoftReference來(lái)描述,指的是那些有用但是不是必須要的對(duì)象。系統(tǒng)在發(fā)生內(nèi)存溢出前會(huì)對(duì)這類引用的對(duì)象進(jìn)行回收。
軟引用可以用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存
軟引用自身不會(huì)被垃圾回收,因?yàn)镚C Root還引用著,軟引用自身需要配合引用隊(duì)列來(lái)釋放。
String str = new String("abc"); // 強(qiáng)引用 SoftReference<String> softRef = new SoftReference<String>(str); // 軟引用 // 當(dāng)內(nèi)存不足時(shí),等價(jià)于: if (JVM.內(nèi)存不足()) {//當(dāng)內(nèi)存不夠的時(shí)候就回收 str = null; // 轉(zhuǎn)換為軟引用 System.gc(); // 垃圾回收器進(jìn)行回收 }
軟引用在實(shí)際中有重要的應(yīng)用:
瀏覽器的后退按鈕。按后退時(shí),這個(gè)后退時(shí)顯示的網(wǎng)頁(yè)內(nèi)容是重新進(jìn)行請(qǐng)求還是從緩存中取出呢?這就要看具體的實(shí)現(xiàn)策略了。
- 獲取頁(yè)面進(jìn)行瀏覽,瀏覽完成后就可以將頁(yè)面設(shè)置為軟應(yīng)用
- 當(dāng)點(diǎn)擊回退按鈕到先前瀏覽過(guò)的頁(yè)面后,判斷是否被垃圾回收機(jī)制回收了,沒(méi)有回收就直接用,如果回收了,就在重新構(gòu)建頁(yè)面
軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,當(dāng)引用的對(duì)象被垃圾回收器回收后,JVM會(huì)自動(dòng)把這個(gè)軟引用加入到和它相關(guān)的這個(gè)引用隊(duì)列中
弱引用(WeakReference)
弱引用可以用WeakReference來(lái)描述,他的強(qiáng)度比軟引用更低一點(diǎn),弱引用的對(duì)象下一次GC的時(shí)候一定會(huì)被回收,而不管內(nèi)存是否足夠。 不過(guò),由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象。
弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。
String str = new String("abc"); //強(qiáng)引用 WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用 str = null; // 當(dāng)垃圾回收器進(jìn)行掃描回收時(shí)等價(jià)于: str = null; System.gc(); // 垃圾回收器進(jìn)行回收 // 下面的代碼會(huì)讓str再次變?yōu)橐粋€(gè)強(qiáng)引用: str = weakRef.get();
使用場(chǎng)景:
- 如果這個(gè)對(duì)象是偶爾的使用,并且希望在使用時(shí)隨時(shí)就能獲取到,但又不想影響此對(duì)象的垃圾收集,那么你應(yīng)該用弱引用來(lái)記住此對(duì)象。
- 當(dāng)你想引用一個(gè)對(duì)象,但是這個(gè)對(duì)象有自己的生命周期,你不想介入這個(gè)對(duì)象的生命周期,這時(shí)候你就可以使用弱引用。這個(gè)引用不會(huì)在對(duì)象的垃圾回收判斷中產(chǎn)生任何附加的影響。
弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對(duì)象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
虛引用(PhantomReference)
虛引用也被稱作幻影引用,是最弱的引用關(guān)系,可以用PhantomReference
來(lái)描述,他必須和ReferenceQueue
一起使用,同樣的當(dāng)發(fā)生GC的時(shí)候,虛引用也會(huì)被回收。
虛引用與其他幾種引用都不同,它并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收。
虛引用與軟引用和弱引用的一個(gè)區(qū)別在于: 虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
應(yīng)用場(chǎng)景:
- 程序可以通過(guò)判斷引用隊(duì)列中是否存在該對(duì)象的虛引用,來(lái)了解這個(gè)對(duì)象是否將要被回收??梢?strong>用來(lái)作為GC回收Object的標(biāo)志。
- 可以用虛引用來(lái)管理堆外內(nèi)存。
Reference和ReferenceQueue
四大引用的父類Reference和ReferenceQueue
在Reference中有5個(gè)非常重要的屬性:referent,next,discovered,pending,queue。
//5個(gè)非常重要的屬性 private T referent; /* Treated specially by GC */ volatile ReferenceQueue<? super T> queue; Reference next; transient private Reference<T> discovered; /* used by VM */ private static Reference<Object> pending = null;
可以把每個(gè)Reference看做一個(gè)節(jié)點(diǎn),多個(gè)Reference通過(guò)next,discovered,pending 進(jìn)行關(guān)聯(lián)
- referent就是Reference實(shí)際引用的對(duì)象。
- 通過(guò)next屬性,可以構(gòu)建ReferenceQueue。
- 通過(guò)discovered屬性,可以構(gòu)建Discovered List。
- 通過(guò)pending屬性,可以構(gòu)建Pending List。
四大狀態(tài)
上圖就是Reference 的四個(gè)狀態(tài)
//Reference的兩個(gè)構(gòu)造方法,一個(gè)帶隊(duì)列,一個(gè)不帶 Reference(T referent) { this(referent, null); } ? Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; } ?
對(duì)于帶ReferenceQueue的Reference,GC會(huì)把要回收對(duì)象的Reference放到ReferenceQueue中,后續(xù)該Reference需要程序員自己處理 (調(diào)用poll方法) 。
不帶ReferenceQueue的Reference, 由GC自己處理,待回收的對(duì)象其Reference狀態(tài)會(huì)變成Inactive。
- 創(chuàng)建好了Reference,就進(jìn)入active狀態(tài)。
- active狀態(tài)下,如果引用對(duì)象的可到達(dá)狀態(tài)發(fā)送變化就會(huì)轉(zhuǎn)變成Inactive或Pending狀態(tài)。
- Inactive狀態(tài)很好理解,到達(dá)Inactive狀態(tài)的Reference狀態(tài)不能被改變,會(huì)等待GC回收。
- Pending狀態(tài)代表等待入Queue,Reference內(nèi)部有個(gè)ReferenceHandler,會(huì)調(diào)用enqueue方法,將Pending對(duì)象入到Queue中入Queue的對(duì)象,其狀態(tài)就變成了Enqueued。
- Enqueued狀態(tài)的對(duì)象,如果調(diào)用poll方法從ReferenceQueue拿出,則該Reference的狀態(tài)就變成了Inactive,等待GC的回收。
這就是Reference的一個(gè)完整的生命周期。
三個(gè)Queue/List
三個(gè)Queue/List:ReferenceQueue,discovered List和pending List
。
- ReferenceQueue它本質(zhì)是由Reference中的next連接而成的。用來(lái)存儲(chǔ)GC待回收的對(duì)象。
- pending List就是待入ReferenceQueue的list。
- discovered List這個(gè)有點(diǎn)特別,在Pending狀態(tài)時(shí)候,discovered List就等于pending List。 在Active狀態(tài)的時(shí)候discovered List實(shí)際上維持的是一個(gè)引用鏈。通過(guò)這個(gè)引用鏈,我們可以獲得引用的鏈?zhǔn)浇Y(jié)構(gòu),當(dāng)某個(gè)Reference狀態(tài)不再是Active狀態(tài)時(shí),需要將這個(gè)Reference從discovered List中刪除。
總結(jié)
Java的四種引用的級(jí)別由高到低依次為:強(qiáng)引用 > 軟引用 > 弱引用 > 虛引用
回收時(shí)機(jī) | 用途 | 生存時(shí)間 | |
---|---|---|---|
強(qiáng) | 重來(lái)不會(huì) | 對(duì)象的一般狀態(tài) | JVM停止運(yùn)行時(shí)終止 |
軟 | 內(nèi)存不足 時(shí) | 聯(lián)合引用隊(duì)列構(gòu)造有效期短、占內(nèi)存大,生命周期長(zhǎng)的對(duì)象的二級(jí)高速緩沖器(內(nèi)存不足的時(shí)候清空) | 內(nèi)存不足 時(shí)終止 |
弱 | 在垃圾回收時(shí) | 聯(lián)合引用隊(duì)列構(gòu)造有效期短、占內(nèi)存大,生命周期長(zhǎng)的對(duì)象的一級(jí)高速緩沖器(發(fā)生GC的時(shí)候清空) | GC運(yùn)行后終止 |
虛 | 在垃圾回收時(shí) | 聯(lián)合引用隊(duì)列來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng) | GC運(yùn)行后終止 |
以上就是詳解Java中的四種引用類型(強(qiáng)軟弱虛)的詳細(xì)內(nèi)容,更多關(guān)于Java引用類型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中EasyExcel實(shí)現(xiàn)execl導(dǎo)入導(dǎo)出
本文主要介紹了SpringBoot中EasyExcel實(shí)現(xiàn)execl導(dǎo)入導(dǎo)出,實(shí)現(xiàn)了如何準(zhǔn)備環(huán)境、創(chuàng)建實(shí)體類、自定義轉(zhuǎn)換器以及編寫導(dǎo)入邏輯的步驟和示例代碼,感興趣的可以了解下2023-06-06springboot docker原理及項(xiàng)目構(gòu)建
這篇文章主要介紹了springboot docker原理及項(xiàng)目構(gòu)建,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11java常用Lambda表達(dá)式使用場(chǎng)景源碼示例
這篇文章主要為大家介紹了java常用Lambda表達(dá)式使用場(chǎng)景源碼示例及應(yīng)用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Spring注解配置實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Spring注解配置實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析
這篇文章主要介紹了Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析,文章通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Spring學(xué)習(xí)筆記3之消息隊(duì)列(rabbitmq)發(fā)送郵件功能
這篇文章主要介紹了Spring學(xué)習(xí)筆記3之消息隊(duì)列(rabbitmq)發(fā)送郵件功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07聊聊Spring?Cloud?Gateway過(guò)濾器精確控制異常返回問(wèn)題
這篇文章主要介紹了Spring?Cloud?Gateway過(guò)濾器精確控制異常返回問(wèn)題,本篇任務(wù)就是分析上述現(xiàn)象的原因,通過(guò)閱讀源碼搞清楚返回碼和響應(yīng)body生成的具體邏輯,需要的朋友可以參考下2021-11-11