在閱讀本文之前最好對(duì) Reference 框架有一個(gè)整體的把握,可以參考我上一篇文章?Reference 框架概覽?;本文主要講了?Reference
?的子類(lèi)實(shí)現(xiàn)和應(yīng)用(SoftReference,WeakReference,PhantomReference
);
創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿(mǎn)足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的寧陽(yáng)網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
Java 引用的強(qiáng)弱關(guān)系:StrongReference > SoftReference > WeakReference > PhantomReference
強(qiáng)引用:我們通常使用的引用,形如Object o = new Object();
此時(shí)從 stack 中的 o,到 heap 中的 Object 就是強(qiáng)引用;其他引用強(qiáng)弱的判定規(guī)則,可以查看我上一篇文章Reference 框架概覽?;
軟引用:可以用來(lái)表示一些有用但非必須的對(duì)象;JVM 會(huì)根據(jù)使用率和剩余堆空間大小來(lái)公共決定什么時(shí)候回收 SoftReference;JVM 保證在拋出 OOM 之前會(huì)再次掃描回收這些軟引用,如果回收后內(nèi)存仍不足才會(huì)拋出 OOM;所以在源碼的注釋中也寫(xiě)了 SoftReference 適合實(shí)現(xiàn)內(nèi)存敏感的緩存;
public?class?SoftReference<T>?extends?Reference<T>?{??/** ???*?Timestamp?clock,?updated?by?the?garbage?collector ???*/ ??static?private?long?clock;??/** ???*?Timestamp?updated?by?each?invocation?of?the?get?method.??The?VM?may?use ???*?this?field?when?selecting?soft?references?to?be?cleared,?but?it?is?not ???*?required?to?do?so. ???*/ ??private?long?timestamp;??public?SoftReference(T?referent)?{????super(referent);????this.timestamp?=?clock; ??}??public?SoftReference(T?referent,?ReferenceQueue<??super?T>?q)?{????super(referent,?q);????this.timestamp?=?clock; ??}??public?T?get()?{ ????T?o?=?super.get();????if?(o?!=?null?&&?this.timestamp?!=?clock)??????this.timestamp?=?clock;????return?o; ??} }
看上面的代碼,SoftReference 與 Reference 相比多了兩個(gè)時(shí)間戳?clock,timestamp
,并且會(huì)在每次?get
的時(shí)候更新時(shí)間戳;
clock:這個(gè)時(shí)間戳是static
修飾的,是所有 SoftReference 共有,由 JVM 維護(hù);
timestamp:主要用于記錄當(dāng)前對(duì)象的存活時(shí)間;
回收策略
上面提到 SoftReference 的回收是由使用率和剩余堆空間大小來(lái)公共決定的,那么它是怎么實(shí)現(xiàn)的呢?
openjdk/hotspot/src/share/vm/memory/referencePolicy.cpp
//?Capture?state?(of-the-VM)?information?needed?to?evaluate?the?policyvoid?LRUCurrentHeapPolicy::setup()?{ ??_max_interval?=?(Universe::get_heap_free_at_last_gc()?/?M)?*?SoftRefLRUPolicyMSPerMB; ??assert(_max_interval?>=?0,"Sanity?check"); }//?The?oop?passed?in?is?the?SoftReference?object,?and?not//?the?object?the?SoftReference?points?to.bool?LRUCurrentHeapPolicy::should_clear_reference(oop?p,?jlong?timestamp_clock)?{ ??jlong?interval?=?timestamp_clock?-?java_lang_ref_SoftReference::timestamp(p); ??assert(interval?>=?0,?"Sanity?check");??//?The?interval?will?be?zero?if?the?ref?was?accessed?since?the?last?scavenge/gc. ??if(interval?<=?_max_interval)?{????return?false; ??}??return?true; }
根據(jù)上面的代碼可以大致知道:
首先計(jì)算出了最大堆內(nèi)存和上次 GC 時(shí)剩余的內(nèi)存;
再用(剩余內(nèi)存 / 最大內(nèi)存 )* SoftRefLRUPolicyMSPerMB 得出到下次 GC 期間軟引用的最大 idle 時(shí)間;
最后用 clock 和 timestamp 兩個(gè)時(shí)間戳差值得到 SoftReference 的 idle 時(shí)間(每次 get 的時(shí)候?this.timestamp = clock;
,所以get 之后 idle 時(shí)間歸零),如果大于最大 idle 時(shí)間則清除;
我們可以簡(jiǎn)單測(cè)試一下,啟動(dòng)參數(shù):-XX:SoftRefLRUPolicyMSPerMB=2 -Xmx10M -XX:+PrintCommandLineFlags -verbose:gc
;
-XX:SoftRefLRUPolicyMSPerMB=2
:可以參照上面的計(jì)算過(guò)程調(diào)節(jié) SoftReference 的回收頻率;
-Xmx10M
:為最大堆內(nèi)存,同樣可以自行調(diào)節(jié),-verbose:gc
:打開(kāi) GC 日志,-XX:+PrintCommandLineFlags
:打印 JVM 啟動(dòng)參數(shù);
private?static?void?test03()?throws?InterruptedException?{ ??ReferenceQueue?queue?=?new?ReferenceQueue(); ??Object?o?=?new?Object()?{????@Override ????public?String?toString()?{??????return?"zhangsan"; ????} ??}; ?? ??Reference?softRef?=?new?SoftReference(o,?queue);??new?Monitor(queue).start(); ?? ??o?=?null; ??System.gc(); ??log.info("o=null,?referent:{}",?softRef.get());?? ??byte[]?bytes?=?new?byte[3?*?1024?*?1024]; ??System.gc(); ??log.info("After?GC,?referent:{}",?softRef.get()); ??Thread.sleep(2000); ??System.gc(); ??log.info("After?GC,?referent:{}",?softRef.get()); }private?static?class?Monitor?extends?Thread?{ ??ReferenceQueue?queue;??public?Monitor(ReferenceQueue?queue)?{????this.queue?=?queue; ??}?? ??@Override ??public?void?run()?{????while?(true)?{??????try?{ ????????log.info("remove?reference:{}",?queue.remove().toString()); ??????}?catch?(InterruptedException?e)?{ ????????e.printStackTrace(); ??????} ????} ??} }
// 打?。?/p>
[main]?o=null,?referent:zhangsan [main]?After?GC,?referent:zhangsan [main]?After?GC,?referent:null[Thread-0]?remove?reference:java.lang.ref.SoftReference@bcffe9a
根據(jù)不同的參數(shù)設(shè)置會(huì)出現(xiàn)不同的情況,大家可以自行調(diào)節(jié)參數(shù),驗(yàn)證上面的計(jì)算規(guī)則;另外如果-XX:SoftRefLRUPolicyMSPerMB=0
,那么 SoftReference 就應(yīng)該和 WeakReference 差不多了,至于是否完全一致,就留到以后查看 JVM 的時(shí)候再確定了;
弱引用:被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次 GC,當(dāng) GC 的時(shí)候無(wú)論內(nèi)存是否足夠,使用是否頻繁都會(huì)被清除;同樣源碼注釋里面也寫(xiě)了 WeakReference 適合實(shí)現(xiàn) canonicalizing mappings,比如 WeakHashMap;
public?class?WeakReference<T>?extends?Reference<T>?{??public?WeakReference(T?referent)?{????super(referent); ??}??public?WeakReference(T?referent,?ReferenceQueue<??super?T>?q)?{????super(referent,?q); ??} }
簡(jiǎn)單測(cè)試,啟動(dòng)參數(shù):-Xmx300M -XX:+PrintCommandLineFlags -verbose:gc
;
private?static?void?test04()?{ ??ReferenceQueue?queue?=?new?ReferenceQueue(); ??Object?o?=?new?Object()?{????@Override ????public?String?toString()?{??????return?"zhangsan"; ????} ??}; ?? ??Reference?ref?=?new?WeakReference(o,?queue);??new?Monitor(queue).start(); ?? ??o?=?null; ??log.info("Before?GC,?referent:{}",?ref.get()); ??System.gc(); ??log.info("After?GC,?referent:{}",?ref.get()); }
// 打?。?/p>
[main]?????Before?GC,?referent:zhangsan[main]?????After?GC,?referent:null[Thread-0]?remove?reference:java.lang.ref.WeakReference@67ac4ff0
可以看到在內(nèi)存足夠的時(shí)候,referent 被清除,WeakReference 在下次 GC 的時(shí)候隨機(jī)被清除,并且 ReferenceQueue 也收到了事件通知;
虛引用:最弱的一種引用關(guān)系,虛引用對(duì)一個(gè)對(duì)象的生命周期完全沒(méi)有影響,設(shè)置虛引用的唯一目的就是得到 referent 被回收的事件通知;
public?class?PhantomReference<T>?extends?Reference<T>?{????public?T?get()?{????????return?null; ????}????public?PhantomReference(T?referent,?ReferenceQueue<??super?T>?q)?{????????super(referent,?q); ????} }
從源碼也能看到 get 的時(shí)候,永遠(yuǎn)返回 null;
同樣簡(jiǎn)單測(cè)試一下,
private?static?void?test06()?{ ??ReferenceQueue?queue?=?new?ReferenceQueue(); ??Object?o?=?new?Object()?{????@Override ????public?String?toString()?{??????return?"zhangsan"; ????} ??}; ?? ??Reference?ref?=?new?PhantomReference(o,?queue);??new?Monitor(queue).start(); ?? ??o?=?null; ??log.info("Before?GC,?referent:{}",?ref.get()); ??System.gc(); ??log.info("After?GC,?referent:{}",?ref.get()); }
// 打印:
[main]?????Before?GC,?referent:null[main]?????After?GC,?referent:null[Thread-0]?remove?reference:java.lang.ref.PhantomReference@661a5fff
可以看到?PhantomReference.get()
?始終為 null,并且當(dāng) referent 被回收的時(shí)候,并且 ReferenceQueue 也收到了事件通知;
此外 PhantomReference 和其他引用還有一個(gè)很大的不同,在 ReferenceQueue 中 JVM 并不會(huì)幫我們把 referent 字段置為空;
private?static?void?test07()?{ ??ReferenceQueue?queue?=?new?ReferenceQueue(); ??Object?o?=?new?Object()?{????@Override ????public?String?toString()?{??????return?"zhangsan"; ????} ??}; ?? ??Reference?ref?=?new?PhantomReference(o,?queue);??new?Monitor2(queue).start(); ?? ??o?=?null; ??log.info("Before?GC,?referent:{}",?ref.get()); ??System.gc(); ??log.info("After?GC,?referent:{}",?ref.get()); }private?static?class?Monitor2?extends?Thread?{ ??ReferenceQueue?queue;?? ??public?Monitor2(ReferenceQueue?queue)?{????this.queue?=?queue; ??}?? ??@Override ??public?void?run()?{????try?{??????while?(true)?{ ????????Reference?ref?=?queue.poll(); ????????log.info("remove?reference:{}",?ref);????????if?(ref?!=?null)?{ ????????Field?field?=?Reference.class.getDeclaredField("referent"); ????????field.setAccessible(true); ???????? ????????log.info("ReferenceQueue?get?Referent:{}",?field.get(ref)); ????????ref.clear();????????break; ????????} ??????} ????}?catch?(Exception?e)?{ ??????e.printStackTrace(); ????} ??} }
// 打?。?/p>
[main]?????Before?GC,?referent:null[main]?????After?GC,?referent:null[Thread-0]?remove?reference:null[Thread-0]?remove?reference:java.lang.ref.PhantomReference@7b4cba2 [Thread-0]?ReferenceQueue?get?Referent:zhangsan
這里可以看到從 ReferenceQueue 中取出來(lái)的 Reference 仍然可以取到引用對(duì)象,即 referent;但是在其他引用中打印為 null,這里可以將上面例子中的 Monitor 改為 Monitor2 測(cè)試;
Cleaner:
在Reference.tryHandlePending()
里面提到的,主要用于替代Object.finalize()
;
public?class?Cleaner?extends?PhantomReference<Object>?{??private?static?final?ReferenceQueue<Object>?dummyQueue?=?new?ReferenceQueue<>();??static?private?Cleaner?first?=?null;??private?Cleaner ????next?=?null, ????prev?=?null;???? ??private?final?Runnable?thunk;?? ??private?Cleaner(Object?referent,?Runnable?thunk)?{????super(referent,?dummyQueue);????this.thunk?=?thunk; ??}?? ??public?static?Cleaner?create(Object?ob,?Runnable?thunk)?{????if?(thunk?==?null)??????return?null;????return?add(new?Cleaner(ob,?thunk)); ??}??private?static?synchronized?Cleaner?add(Cleaner?cl)?{????if?(first?!=?null)?{ ??????cl.next?=?first; ??????first.prev?=?cl; ????} ????first?=?cl;????return?cl; ??}??private?static?synchronized?boolean?remove(Cleaner?cl)?{?}??public?void?clean()?{????if?(!remove(this))??????return;????try?{ ??????thunk.run(); ????}?catch?(final?Throwable?x)?{ ??????AccessController.doPrivileged(new?PrivilegedAction<Void>()?{??????????public?Void?run()?{????????????if?(System.err?!=?null)??????????????new?Error("Cleaner?terminated?abnormally",?x) ????????????????.printStackTrace(); ????????????System.exit(1);????????????return?null; ??????????}}); ????} ??} }
從代碼可以看到,
Cleaner 只能通過(guò)工廠方法創(chuàng)建,并且所有的 Cleaner 都共同屬于同一個(gè) Reference 鏈表;
代碼中的next、prev
不同于 Reference 中的 next,他們組成了一個(gè)雙向鏈表;
Cleaner 中沒(méi)有入隊(duì)操作,在創(chuàng)建之初就已經(jīng)加入鏈表了,具體代碼可以查看Reference.tryHandlePending()
;
ReferenceQueue(dummyQueue
?域)的作用不再是提供入隊(duì)和事件監(jiān)聽(tīng)功能,而僅僅是保證 GC 不會(huì)自動(dòng)將 Cleaner 給回收了;
Cleaner 的主要邏輯就是傳入一個(gè) clean 線(xiàn)程,在 referent 引用對(duì)象清除的時(shí)候,執(zhí)行?clean
?操作;
對(duì)于上面講的軟引用、弱引用、虛引用,都有一套共同的事件通知機(jī)制,具體邏輯在 Reference 類(lèi)中;主要的差別在于引用回收條件的判斷,這部分代碼在 JVM 里面;
另外對(duì)于 Reference 類(lèi)還有 FinalReference 沒(méi)有寫(xiě),主要用于當(dāng)類(lèi)重寫(xiě)finalize
方法時(shí),JVM 會(huì)將他包裝在 FinalReference 里面,里面的細(xì)節(jié)比較多,并且一般不建議使用,所以暫時(shí)沒(méi)寫(xiě);
此外《Effective Java》第三版的第八條也講了避免使用finalizer和cleaner;詳情可以自行查閱;
當(dāng)前題目:JDK源碼分析(8)之Reference完全解讀
URL網(wǎng)址:http://www.muchs.cn/article48/ghpohp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、網(wǎng)站維護(hù)、網(wǎng)站內(nèi)鏈、標(biāo)簽優(yōu)化、小程序開(kāi)發(fā)、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)