我們都知道,在JVM垃圾收集中,GC判斷堆中對(duì)象實(shí)例或數(shù)據(jù)是否垃圾的方法有參考計(jì)數(shù)法和可達(dá)性算法。
無(wú)論是通過(guò)參考計(jì)數(shù)算法確定對(duì)象的參考數(shù),還是通過(guò)根搜索算法確定對(duì)象的參考鏈?zhǔn)欠窨梢缘竭_(dá),對(duì)象的生存與否都與“參考”有關(guān)。
引用
首先是引文,Java的引文,類似于C語(yǔ)言的指針。首次學(xué)習(xí)Java時(shí),Java數(shù)據(jù)類型分為兩大類:基本類型和引用類型。
基本類型:編程語(yǔ)言中內(nèi)置的最小粒度的數(shù)據(jù)類型。其中包括四個(gè)主要類別八種類型。
四種整數(shù)類型:Byte、short、int和long
Float、double的兩種浮點(diǎn)數(shù)類型
1種字符類型:char
一種布爾類型:布爾
參照類型:參照類型是指物件,而不是原始值,指向物件的變數(shù)是參照變數(shù)。在Java中,除了基本類型外,其他類型都是參考類型,主要包括類、接口、數(shù)組、枚舉、注釋等。
通過(guò)數(shù)據(jù)類型,JVM標(biāo)準(zhǔn)化了程序數(shù)據(jù)管理。不同的數(shù)據(jù)類型有不同的存儲(chǔ)形式和位置。
如何偏離,回到正題,通過(guò)引用可以操縱堆里的對(duì)象。引用《Java編程思想》的一段話。
“每種編程語(yǔ)言都有自己的數(shù)據(jù)處理方法。有時(shí)候程序員要注意要處理的數(shù)據(jù)是什么類型的。是直接操作元素,還是基于特殊語(yǔ)法(如C/C中的指針)的間接表示來(lái)操作對(duì)象?所有這些都在Java中被簡(jiǎn)化,一切都被視為對(duì)象。因此,我們可以采用統(tǒng)一的語(yǔ)法。把所有東西都當(dāng)作對(duì)象,但操縱的標(biāo)識(shí)符實(shí)際上指向一個(gè)對(duì)象的參照。”?!?
例如:
Person person=new Person('章3 ');
其中person是對(duì)person實(shí)例“張三”的引用。我們通常通過(guò)Person操作“張三”實(shí)例。
在JDK 1.2之前,Java的參考定義是傳統(tǒng)的。如果存儲(chǔ)在reference類型的數(shù)據(jù)中的數(shù)值表示另一個(gè)內(nèi)存的起始地址,則refrence數(shù)據(jù)是表示一個(gè)內(nèi)存、一個(gè)對(duì)象的引用。這種定義很純粹,但太狹隘了。一個(gè)對(duì)象在這種定義中只有被引用或沒(méi)有被引用的狀態(tài),對(duì)于如何描述“食物的味道,放棄的可惜”對(duì)象,似乎無(wú)能為力。
例如,如果有足夠的內(nèi)存空間,則希望說(shuō)明可以保留在內(nèi)存中的類對(duì)象。垃圾收集后,如果內(nèi)存仍然很緊,可以扔掉這些對(duì)象。(威廉莎士比亞、垃圾收集、垃圾收集、垃圾收集、垃圾收集)很多系統(tǒng)的緩存功能都符合這些應(yīng)用場(chǎng)景。
JDK 1.2之后,Java擴(kuò)展了引用的概念,使引用成為
「強(qiáng)參照」(Strong Reference)「軟參照」(Soft Reference)「弱參照」(Weak Reference)「虛擬參照」(Phantom Reference)的四個(gè)參照強(qiáng)度會(huì)依序減少。
在Java中引入四個(gè)引用的目的是使程序能夠自行確定對(duì)象的生命周期。JVM是通過(guò)垃圾收集器對(duì)這四個(gè)引用進(jìn)行不同處理,從而改變對(duì)象生命周期。
JDK 8的UML圖
FinalReference類在包中可見(jiàn),其他三種引用類型都是public,可以直接在應(yīng)用程序中使用。
姜仁龍
Java中最常見(jiàn)的是強(qiáng)引用。將對(duì)象指定給參照變量。這個(gè)參照變量是強(qiáng)參照。引用,例如“Object obj=new Object()”。
強(qiáng)引用變量引用的對(duì)象處于無(wú)法被垃圾收集器回收的到達(dá)狀態(tài),即使該對(duì)象永遠(yuǎn)不使用,也不會(huì)被回收。
內(nèi)存不足時(shí),JVM開(kāi)始垃圾收集,對(duì)于強(qiáng)忍使用的對(duì)象,即使出現(xiàn)OOM,也不回收該對(duì)象,打也不接收。(約翰f肯尼迪,美國(guó)電視劇)因此,強(qiáng)烈的引用有時(shí)是Java內(nèi)存泄漏的原因之一。
對(duì)于一般物件,如果沒(méi)有其他參考關(guān)系,只要超出參考的范圍,或?qū)?duì)應(yīng)的(強(qiáng))參考標(biāo)記為null,垃圾收集器通常會(huì)認(rèn)為可以回收。(具體回收時(shí)間取決于垃圾收集戰(zhàn)略。)。
Coding~
publicclassstrongrefenencedemo {
Publicstaticvoid
?main(String[]?args)?{ ????????Object?o1?=?new?Object(); ????????Object?o2?=?o1; ????????o1?=?null; ????????Sy(); ????????Sy(o1);??//null ????????Sy(o2);?? ????} }demo 中盡管 o1已經(jīng)被回收,但是 o2 強(qiáng)引用 o1,一直存在,所以不會(huì)被GC回收
軟引用
軟引用是一種相對(duì)強(qiáng)引用弱化了一些的引用,需要用java.lang.ref.SoftReference 類來(lái)實(shí)現(xiàn),可以讓對(duì)象豁免一些垃圾收集。
軟引用用來(lái)描述一些還有用,但并非必需的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中并進(jìn)行第二次回收。如果這次回收還是沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。
對(duì)于只有軟引用的對(duì)象來(lái)說(shuō):當(dāng)系統(tǒng)內(nèi)存充足時(shí)它不會(huì)被回收,當(dāng)系統(tǒng)內(nèi)存不足時(shí)它才會(huì)被回收。
coding~
//VM?options:?-Xms5m?-Xmx5m
public?class?SoftRefenenceDemo?{
????public?static?void?main(String[]?args)?{
????????softRefMemoryEnough();
????????Sy("------內(nèi)存不夠用的情況------");
????????softRefMemoryNotEnough();
????}
????private?static?void?softRefMemoryEnough()?{
????????Object?o1?=?new?Object();
????????SoftReference<Object>?s1?=?new?SoftReference<Object>(o1);
????????Sy(o1);
????????Sy());
????????o1?=?null;
????????Sy();
????????Sy(o1);
????????Sy());
????}
?????/**
?????*?JVM配置`-Xms5m?-Xmx5m`?,然后故意new一個(gè)一個(gè)大對(duì)象,使內(nèi)存不足產(chǎn)生?OOM,看軟引用回收情況
?????*/
????private?static?void?softRefMemoryNotEnough()?{
????????Object?o1?=?new?Object();
????????SoftReference<Object>?s1?=?new?SoftReference<Object>(o1);
????????Sy(o1);
????????Sy());
????????o1?=?null;
????????byte[]?bytes?=?new?byte[10?*?1024?*?1024];
????????Sy(o1);
????????Sy());
????}
}
Output
java.lang.Object@2503dbd3
java.lang.Object@2503dbd3
null
java.lang.Object@2503dbd3
------內(nèi)存不夠用的情況------
java.lang.Object@4b67cf4d
java.lang.Object@4b67cf4d
java.lang.OutOfMemoryError: Java heap space
at re(SoftRefenenceDemo.java:42)
at re(SoftRefenenceDemo.java:15)
null
null
軟引用通常用在對(duì)內(nèi)存敏感的程序中,比如高速緩存就有用到軟引用,內(nèi)存夠用的時(shí)候就保留,不夠用就回收。
我們看下 Mybatis 緩存類 SoftCache 用到的軟引用
public?Object?getObject(Object?key)?{
????Object?result?=?null;
????SoftReference<Object>?softReference?=?(SoftReference(key);
????if?(softReference?!=?null)?{
????????result?=?();
????????if?(result?==?null)?{
????????????(key);
????????}?else?{
????????????synchronized)?{
????????????????(result);
????????????????if?.size()?>?)?{
????????????????????();
????????????????}
????????????}
????????}
????}
????return?result;
}
弱引用
弱引用也是用來(lái)描述非必需對(duì)象的,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。
弱引用需要用java.lang.ref.WeakReference類來(lái)實(shí)現(xiàn),它比軟引用的生存期更短。
對(duì)于只有弱引用的對(duì)象來(lái)說(shuō),只要垃圾回收機(jī)制一運(yùn)行,不管 JVM 的內(nèi)存空間是否足夠,都會(huì)回收該對(duì)象占用的內(nèi)存。
coding~
public?class?WeakReferenceDemo?{
????public?static?void?main(String[]?args)?{
????????Object?o1?=?new?Object();
????????WeakReference<Object>?w1?=?new?WeakReference<Object>(o1);
????????Sy(o1);
????????Sy());
????????o1?=?null;
????????Sy();
????????Sy(o1);
????????Sy());
????}
}
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.
官方文檔這么寫(xiě)的,弱引用常被用來(lái)實(shí)現(xiàn)規(guī)范化映射,JDK 中的 WeakHashMap 就是一個(gè)這樣的例子
面試官:既然你都知道弱引用,那能說(shuō)說(shuō) WeakHashMap 嗎
public?class?WeakHashMapDemo?{
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????myHashMap();
????????myWeakHashMap();
????}
????public?static?void?myHashMap()?{
????????HashMap<String,?String>?map?=?new?HashMap<String,?String>();
????????String?key?=?new?String("k1");
????????String?value?=?"v1";
????????map.put(key,?value);
????????Sy(map);
????????key?=?null;
????????Sy();
????????Sy(map);
????}
????public?static?void?myWeakHashMap()?throws?InterruptedException?{
????????WeakHashMap<String,?String>?map?=?new?WeakHashMap<String,?String>();
????????//String?key?=?"weak";
????????//?剛開(kāi)始寫(xiě)成了上邊的代碼
????????//思考一下,寫(xiě)成上邊那樣會(huì)怎么樣??那可不是引用了
????????String?key?=?new?String("weak");
????????String?value?=?"map";
????????map.put(key,?value);
????????Sy(map);
????????//去掉強(qiáng)引用
????????key?=?null;
????????Sy();
????????T(1000);
????????Sy(map);
????}
}
我們看下 ThreadLocal 中用到的弱引用
static?class?ThreadLocalMap?{
????static?class?Entry?extends?WeakReference<ThreadLocal<?>>?{
????????Object?value;
????????Entry(ThreadLocal<?>?k,?Object?v)?{
????????????super(k);
????????????value?=?v;
????????}
????}
????//......
}
虛引用
虛引用也稱為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關(guān)系。
虛引用,顧名思義,就是形同虛設(shè),與其他幾種引用都不太一樣,一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例。
虛引用需要java.lang.ref.PhantomReference 來(lái)實(shí)現(xiàn)。
如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收,它不能單獨(dú)使用也不能通過(guò)它訪問(wèn)對(duì)象,虛引用必須和引用隊(duì)列(RefenenceQueue)聯(lián)合使用。
虛引用的主要作用是跟蹤對(duì)象垃圾回收的狀態(tài)。僅僅是提供了一種確保對(duì)象被 finalize 以后,做某些事情的機(jī)制。
PhantomReference 的 get 方法總是返回 null,因此無(wú)法訪問(wèn)對(duì)應(yīng)的引用對(duì)象。其意義在于說(shuō)明一個(gè)對(duì)象已經(jīng)進(jìn)入 finalization 階段,可以被 GC 回收,用來(lái)實(shí)現(xiàn)比 finalization 機(jī)制更靈活的回收操作。
換句話說(shuō),設(shè)置虛引用的唯一目的,就是在這個(gè)對(duì)象被回收器回收的時(shí)候收到一個(gè)系統(tǒng)通知或者后續(xù)添加進(jìn)一步的處理。
Java 允許使用 finalize() 方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。
public?class?PhantomReferenceDemo?{
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????Object?o1?=?new?Object();
????????ReferenceQueue<Object>?referenceQueue?=?new?ReferenceQueue<Object>();
????????PhantomReference<Object>?phantomReference?=?new?PhantomReference<Object>(o1,referenceQueue);
????????Sy(o1);
????????Sy());
????????Sy());
????????o1?=?null;
????????Sy();
????????T(3000);
????????Sy(o1);
????????Sy());?//引用隊(duì)列中
????????Sy());
????}
}
java.lang.Object@4554617c
null
null
null
java.lang.ref.PhantomReference@74a14482
null
引用隊(duì)列
ReferenceQueue 是用來(lái)配合引用工作的,沒(méi)有ReferenceQueue 一樣可以運(yùn)行。
SoftReference、WeakReference、PhantomReference 都有一個(gè)可以傳遞 ReferenceQueue 的構(gòu)造器。
創(chuàng)建引用的時(shí)候,可以指定關(guān)聯(lián)的隊(duì)列,當(dāng) GC 釋放對(duì)象內(nèi)存的時(shí)候,會(huì)將引用加入到引用隊(duì)列。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng),這相當(dāng)于是一種通知機(jī)制。
當(dāng)關(guān)聯(lián)的引用隊(duì)列中有數(shù)據(jù)的時(shí)候,意味著指向的堆內(nèi)存中的對(duì)象被回收。通過(guò)這種方式,JVM 允許我們?cè)趯?duì)象被銷毀后,做一些我們自己想做的事情。
最后,稍微了解下源碼中的實(shí)現(xiàn)
Reference源碼(JDK8)
強(qiáng)軟弱虛四種引用,我們有了個(gè)大概的認(rèn)識(shí),我們也知道除了強(qiáng)引用沒(méi)有對(duì)應(yīng)的類型表示,是普遍存在的。剩下的三種引用都是 java.lang.ref.Reference 的直接子類。
那就會(huì)有疑問(wèn)了,我們可以通過(guò)繼承 Reference,自定義引用類型嗎?
Abstract base class for reference objects. This class defines the operations common to all reference objects. Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.
JDK 官方文檔是這么說(shuō)的,Reference是所有引用對(duì)象的基類。這個(gè)類定義了所有引用對(duì)象的通用操作。因?yàn)橐脤?duì)象是與垃圾收集器緊密協(xié)作而實(shí)現(xiàn)的,所以這個(gè)類可能不能直接子類化。
Reference 的4種狀態(tài)
- Active:新創(chuàng)建的引用實(shí)例處于Active狀態(tài),但當(dāng)GC檢測(cè)到該實(shí)例引用的實(shí)際對(duì)象的可達(dá)性發(fā)生某些改變(實(shí)際對(duì)象處于 GC roots 不可達(dá))后,它的狀態(tài)將變化為Pending或者Inactive。如果 Reference 注冊(cè)了ReferenceQueue,則會(huì)切換為Pending,并且Reference會(huì)加入pending-Reference鏈表中,如果沒(méi)有注冊(cè)ReferenceQueue,會(huì)切換為Inactive。
- Pending:當(dāng)引用實(shí)例被放置在pending-Reference 鏈表中時(shí),它處于Pending狀態(tài)。此時(shí),該實(shí)例在等待一個(gè)叫Reference-handler的線程將此實(shí)例進(jìn)行enqueue操作。如果某個(gè)引用實(shí)例沒(méi)有注冊(cè)在一個(gè)引用隊(duì)列中,該實(shí)例將永遠(yuǎn)不會(huì)進(jìn)入Pending狀態(tài)
- Enqueued:在ReferenceQueue隊(duì)列中的Reference的狀態(tài),如果Reference從隊(duì)列中移除,會(huì)進(jìn)入Inactive狀態(tài)
- Inactive:一旦某個(gè)引用實(shí)例處于Inactive狀態(tài),它的狀態(tài)將不再會(huì)發(fā)生改變,同時(shí)說(shuō)明該引用實(shí)例所指向的實(shí)際對(duì)象一定會(huì)被GC所回收
Reference的構(gòu)造函數(shù)和成員變量
public?abstract?class?Reference<T>?{
???//引用指向的對(duì)象
???private?T?referent;????
???//?reference被回收后,當(dāng)前Reference實(shí)例會(huì)被添加到這個(gè)隊(duì)列中
???volatile?ReferenceQueue<??super?T>?queue;
???//下一個(gè)Reference實(shí)例的引用,Reference實(shí)例通過(guò)此構(gòu)造單向的鏈表
???volatile?Reference?next;
???//由transient修飾,基于狀態(tài)表示不同鏈表中的下一個(gè)待處理的對(duì)象,主要是pending-reference列表的下一個(gè)元素,通過(guò)JVM直接調(diào)用賦值
???private?transient?Reference<T>?discovered;
???//?等待加入隊(duì)列的引用列表,這里明明是個(gè)Reference類型的對(duì)象,官方文檔確說(shuō)是個(gè)list?
???//因?yàn)镚C檢測(cè)到某個(gè)引用實(shí)例指向的實(shí)際對(duì)象不可達(dá)后,會(huì)將該pending指向該引用實(shí)例,
???//discovered字段則是用來(lái)表示下一個(gè)需要被處理的實(shí)例,因此我們只要不斷地在處理完當(dāng)前pending之后,將discovered指向的實(shí)例賦予給pending即可。所以這個(gè)pending就相當(dāng)于是一個(gè)鏈表。
???private?static?Reference<Object>?pending?=?null;
????
????/*?--?Constructors?--?*/
????Reference(T?referent)?{
????????this(referent,?null);
????}
????Reference(T?referent,?ReferenceQueue<??super?T>?queue)?{
?????????=?referent;
?????????=?(queue?==?null)???Re?:?queue;
????}
}
Reference 提供了兩個(gè)構(gòu)造器,一個(gè)帶引用隊(duì)列 ReferenceQueue,一個(gè)不帶。
帶 ReferenceQueue 的意義在于我們可以從外部通過(guò)對(duì) ReferenceQueue 的操作來(lái)了解到引用實(shí)例所指向的實(shí)際對(duì)象是否被回收了,同時(shí)我們也可以通過(guò) ReferenceQueue 對(duì)引用實(shí)例進(jìn)行一些額外的操作;但如果我們的引用實(shí)例在創(chuàng)建時(shí)沒(méi)有指定一個(gè)引用隊(duì)列,那我們要想知道實(shí)際對(duì)象是否被回收,就只能夠不停地輪詢引用實(shí)例的get() 方法是否為空了。
值得注意的是虛引用 PhantomReference,由于它的 get() 方法永遠(yuǎn)返回 null,因此它的構(gòu)造函數(shù)必須指定一個(gè)引用隊(duì)列。
這兩種查詢實(shí)際對(duì)象是否被回收的方法都有應(yīng)用,如 WeakHashMap 中就選擇去查詢 queue 的數(shù)據(jù),來(lái)判定是否有對(duì)象將被回收;而 ThreadLocalMap,則采用判斷 get() 是否為 null 來(lái)作處理。
實(shí)例方法(和ReferenceHandler線程不相關(guān)的方法)
private?static?Lock?lock?=?new?Lock();
//?獲取持有的referent實(shí)例
public?T?get()?{
????return?;
}
//?把持有的referent實(shí)例置為null
public?void?clear()?{
?????=?null;
}
//?判斷是否處于enqeued狀態(tài)
public?boolean?isEnqueued()?{
????return?(?==?Re);
}
//?入隊(duì)參數(shù),同時(shí)會(huì)把referent置為null
public?boolean?enqueue()?{
????return?.enqueue(this);
}
ReferenceHandler線程
通過(guò)上文的討論,我們知道一個(gè)Reference實(shí)例化后狀態(tài)為Active,其引用的對(duì)象被回收后,垃圾回收器將其加入到pending-Reference鏈表,等待加入ReferenceQueue。
ReferenceHandler線程是由Reference靜態(tài)代碼塊中建立并且運(yùn)行的線程,它的運(yùn)行方法中依賴了比較多的本地(native)方法,ReferenceHandler線程的主要功能就pending list中的引用實(shí)例添加到引用隊(duì)列中,并將pending指向下一個(gè)引用實(shí)例。
//?控制垃圾回收器操作與Pending狀態(tài)的Reference入隊(duì)操作不沖突執(zhí)行的全局鎖
//?垃圾回收器開(kāi)始一輪垃圾回收前要獲取此鎖
//?所以所有占用這個(gè)鎖的代碼必須盡快完成,不能生成新對(duì)象,也不能調(diào)用用戶代碼
static?private?class?Lock?{?}
private?static?Lock?lock?=?new?Lock();
private?static?class?ReferenceHandler?extends?Thread?{
????private?static?void?ensureClassInitialized(Class<?>?clazz)?{
????????try?{
????????????Cla(),?true,?clazz.getClassLoader());
????????}?catch?(ClassNotFoundException?e)?{
????????????throw?(Error)?new?NoClassDefFoundError()).initCause(e);
????????}
????}
????static?{
????????ensureClassInitialized);
????????ensureClassInitialized);
????}
????ReferenceHandler(ThreadGroup?g,?String?name)?{
????????super(g,?name);
????}
????public?void?run()?{
????????while?(true)?{
????????????tryHandlePending(true);
????????}
????}
}
static?boolean?tryHandlePending(boolean?waitForNotify)?{
????Reference<Object>?r;
????Cleaner?c;
????try?{
????????synchronized?(lock)?{
????????????//?判斷pending-Reference鏈表是否有數(shù)據(jù)
????????????if?(pending?!=?null)?{
????????????????//?如果有Pending?Reference,從列表中取出
????????????????r?=?pending;
????????????????c?=?r?instanceof?Cleaner???(Cleaner)?r?:?null;
????????????????//?unlink?'r'?from?'pending'?chain
????????????????pending?=?r.discovered;
????????????????r.discovered?=?null;
????????????}?else?{
????//?如果沒(méi)有Pending?Reference,調(diào)用wait等待
????????????????if?(waitForNotify)?{
????????????????????lock.wait();
????????????????}
????????????????//?retry?if?waited
????????????????return?waitForNotify;
????????????}
????????}
????}?catch?(OutOfMemoryError?x)?{
????????T();
????????return?true;
????}?catch?(InterruptedException?x)?{
????????return?true;
????}
????//?Fast?path?for?cleaners
????if?(c?!=?null)?{
????????c.clean();
????????return?true;
????}
????ReferenceQueue<??super?Object>?q?=?r.queue;
????if?(q?!=?Re)?q.enqueue(r);
????return?true;
}
//ReferenceHandler線程是在Reference的static塊中啟動(dòng)的
static?{
????//?ThreadGroup繼承當(dāng)前執(zhí)行線程(一般是主線程)的線程組
????ThreadGroup?tg?=?T().getThreadGroup();
????for?(ThreadGroup?tgn?=?tg;
?????????tgn?!=?null;
?????????tg?=?tgn,?tgn?=?());
????//?創(chuàng)建線程實(shí)例,命名為Reference?Handler,配置最高優(yōu)先級(jí)和后臺(tái)運(yùn)行(守護(hù)線程),然后啟動(dòng)
????Thread?handler?=?new?ReferenceHandler(tg,?"Reference?Handler");
????//?ReferenceHandler線程有最高優(yōu)先級(jí)
????);
????(true);
????();
????//?provide?access?in?SharedSecrets
????S(new?JavaLangRefAccess()?{
????????@Override
????????public?boolean?tryHandlePendingReference()?{
????????????return?tryHandlePending(false);
????????}
????});
}
由于ReferenceHandler線程是Reference的靜態(tài)代碼創(chuàng)建的,所以只要Reference這個(gè)父類被初始化,該線程就會(huì)創(chuàng)建和運(yùn)行,由于它是守護(hù)線程,除非 JVM 進(jìn)程終結(jié),否則它會(huì)一直在后臺(tái)運(yùn)行(注意它的run()方法里面使用了死循環(huán))。
ReferenceQueue源碼
public?class?ReferenceQueue<T>?{
????public?ReferenceQueue()?{?}
?//?內(nèi)部類Null類繼承自ReferenceQueue,覆蓋了enqueue方法返回false
????private?static?class?Null<S>?extends?ReferenceQueue<S>?{
????????boolean?enqueue(Reference<??extends?S>?r)?{
????????????return?false;
????????}
????}
??//?用于標(biāo)識(shí)沒(méi)有注冊(cè)Queue
????static?ReferenceQueue<Object>?NULL?=?new?Null<>();
????//?用于標(biāo)識(shí)已經(jīng)處于對(duì)應(yīng)的Queue中
????static?ReferenceQueue<Object>?ENQUEUED?=?new?Null<>();
????//?靜態(tài)內(nèi)部類,作為鎖對(duì)象
????static?private?class?Lock?{?};
????/*?互斥鎖,用于同步ReferenceHandler的enqueue和用戶線程操作的remove和poll出隊(duì)操作?*/
????private?Lock?lock?=?new?Lock();
????//?引用鏈表的頭節(jié)點(diǎn)
????private?volatile?Reference<??extends?T>?head?=?null;
????//?引用隊(duì)列長(zhǎng)度,入隊(duì)則增加1,出隊(duì)則減少1
????private?long?queueLength?=?0;
????//?入隊(duì)操作,只會(huì)被Reference實(shí)例調(diào)用
????boolean?enqueue(Reference<??extends?T>?r)?{?/*?Called?only?by?Reference?class?*/
????????synchronized?(lock)?{
???//?如果引用實(shí)例持有的隊(duì)列為Re或者Re則入隊(duì)失敗返回false
????????????ReferenceQueue<?>?queue?=?r.queue;
????????????if?((queue?==?NULL)?||?(queue?==?ENQUEUED))?{
????????????????return?false;
????????????}
????????????assert?queue?==?this;
????????????//?當(dāng)前引用實(shí)例已經(jīng)入隊(duì),那么它本身持有的引用隊(duì)列實(shí)例置為Re
????????????r.queue?=?ENQUEUED;
????????????//?如果鏈表沒(méi)有元素,則此引用實(shí)例直接作為頭節(jié)點(diǎn),否則把前一個(gè)引用實(shí)例作為下一個(gè)節(jié)點(diǎn)
????????????r.next?=?(head?==?null)???r?:?head;
????????????//?當(dāng)前實(shí)例更新為頭節(jié)點(diǎn),也就是每一個(gè)新入隊(duì)的引用實(shí)例都是作為頭節(jié)點(diǎn),已有的引用實(shí)例會(huì)作為后繼節(jié)點(diǎn)
????????????head?=?r;
????????????//?隊(duì)列長(zhǎng)度增加1
????????????queueLength++;
????????????//?特殊處理FinalReference,VM進(jìn)行計(jì)數(shù)
????????????if?(r?instanceof?FinalReference)?{
????????????????(1);
????????????}
????????????//?喚醒所有等待的線程
????????????lock.notifyAll();
????????????return?true;
????????}
????}
????//?引用隊(duì)列的poll操作,此方法必須在加鎖情況下調(diào)用
????private?Reference<??extends?T>?reallyPoll()?{???????/*?Must?hold?lock?*/
????????Reference<??extends?T>?r?=?head;
????????if?(r?!=?null)?{
????????????@SuppressWarnings("unchecked")
????????????Reference<??extends?T>?rn?=?r.next;
????????????//?更新next節(jié)點(diǎn)為頭節(jié)點(diǎn),如果next節(jié)點(diǎn)為自身,說(shuō)明已經(jīng)走過(guò)一次出隊(duì),則返回null
????????????head?=?(rn?==?r)???null?:?rn;
????????????r.queue?=?NULL;
????????????//?當(dāng)前頭節(jié)點(diǎn)變更為環(huán)狀隊(duì)列,考慮到FinalReference尚為inactive和避免重復(fù)出隊(duì)的問(wèn)題
????????????r.next?=?r;
????????????//?隊(duì)列長(zhǎng)度減少1
????????????queueLength--;
????????????if?(r?instanceof?FinalReference)?{
????????????????(-1);
????????????}
????????????return?r;
????????}
????????return?null;
????}
????//?隊(duì)列的公有poll操作,主要是加鎖后調(diào)用reallyPoll
????public?Reference<??extends?T>?poll()?{
????????if?(head?==?null)
????????????return?null;
????????synchronized?(lock)?{
????????????return?reallyPoll();
????????}
????}
//?移除引用隊(duì)列中的下一個(gè)引用元素,實(shí)際上也是依賴于reallyPoll的Object提供的阻塞機(jī)制
????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();
????????????if?(r?!=?null)?return?r;
????????????long?start?=?(timeout?==?0)???0?:?Sy();
????????????for?(;;)?{
????????????????lock.wait(timeout);
????????????????r?=?reallyPoll();
????????????????if?(r?!=?null)?return?r;
????????????????if?(timeout?!=?0)?{
????????????????????long?end?=?Sy();
????????????????????timeout?-=?(end?-?start)?/?1000_000;
????????????????????if?(timeout?<=?0)?return?null;
????????????????????start?=?end;
????????????????}
????????????}
????????}
????}
????public?Reference<??extends?T>?remove()?throws?InterruptedException?{
????????return?remove(0);
????}
????void?forEach(Consumer<??super?Reference<??extends?T>>?action)?{
????????for?(Reference<??extends?T>?r?=?head;?r?!=?null;)?{
????????????ac(r);
????????????@SuppressWarnings("unchecked")
????????????Reference<??extends?T>?rn?=?r.next;
????????????if?(rn?==?r)?{
????????????????if??==?ENQUEUED)?{
????????????????????//?still?enqueued?->?we?reached?end?of?chain
????????????????????r?=?null;
????????????????}?else?{
????????????????????//?already?dequeued:?r.queue?==?NULL;?->
????????????????????//?restart?from?head?when?overtaken?by?queue?poller(s)
????????????????????r?=?head;
????????????????}
????????????}?else?{
????????????????//?next?in?chain
????????????????r?=?rn;
????????????}
????????}
????}
}
ReferenceQueue只存儲(chǔ)了Reference鏈表的頭節(jié)點(diǎn),真正的Reference鏈表的所有節(jié)點(diǎn)是存儲(chǔ)在Reference實(shí)例本身,通過(guò)屬性 next 拼接的,ReferenceQueue提供了對(duì)Reference鏈表的入隊(duì)、poll、remove等操作。
參考與感謝
《深入理解java虛擬機(jī)》
如果有大數(shù)據(jù)方面的問(wèn)題,或者需要學(xué)習(xí)線路圖或隨堂筆記,評(píng)論區(qū)回復(fù)111。
1.《【cfoutofmemory怎么解決】阿里面試:說(shuō)說(shuō)強(qiáng)引用、軟引用、弱引用、虛引用。》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識(shí),僅代表作者本人觀點(diǎn),與本網(wǎng)站無(wú)關(guān),侵刪請(qǐng)聯(lián)系頁(yè)腳下方聯(lián)系方式。
2.《【cfoutofmemory怎么解決】阿里面試:說(shuō)說(shuō)強(qiáng)引用、軟引用、弱引用、虛引用。》僅供讀者參考,本網(wǎng)站未對(duì)該內(nèi)容進(jìn)行證實(shí),對(duì)其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。
3.文章轉(zhuǎn)載時(shí)請(qǐng)保留本站內(nèi)容來(lái)源地址,http://f99ss.com/gl/2585198.html