Reference類簡介

1 狀態(tài)機(jī)

狀態(tài)轉(zhuǎn)移圖

@startuml
[*] -> active: 新建
active -down-> inactive: 可達(dá)性改變且無注冊隊列
active -right-> pending: 可達(dá)性改變且有注冊隊列
pending -down-> enqueued: 成功加入隊列
enqueued -left-> inactive: 從隊列移除
inactive -left-> [*]
@enduml
狀態(tài)轉(zhuǎn)移圖

說明:

  1. active岔绸。此狀態(tài)需要收集器進(jìn)行特殊處理。當(dāng)收集器檢測到referent的可達(dá)性發(fā)生改變橡伞,它會將實例的狀態(tài)改為為pendinginactive盒揉,這取決于實例在創(chuàng)建時是否已注冊到某個隊列。有注冊隊列情況兑徘,實例會添加到pending-Reference列表中刚盈,等待入隊。新建引用實例是活動狀態(tài)挂脑。
  2. pending藕漱。表示pending-Reference列表的元素,等待被Reference-handler處理并入隊崭闲。未注冊引用實例不會有此狀態(tài)肋联。。
  3. enqueued刁俭。表示在注冊隊列中的元素橄仍。當(dāng)實例從ReferenceQueue中移除時,它將變?yōu)?code>inactive牍戚。未注冊引用實例不會有此狀態(tài)侮繁。盡管Reference提供了入隊方法,但是收集器執(zhí)行的入隊操作是直接執(zhí)行的翘魄,而不是調(diào)用Reference的入隊方法鼎天。
  4. inactive。終止態(tài)暑竟,不會在改變斋射。

狀態(tài)編碼
引用的狀態(tài)是通過不同字段值共同體現(xiàn)的育勺。

狀態(tài) queue next discovered
active 有注冊隊列時ReferenceQueue實例
或者 未注冊隊列時ReferenceQueue.NULL
NULL 下一個discovered列表元素
或者如果是隊尾元素則是this
pending 引用所注冊的ReferenceQueue實例 this 下一個pending列表元素
或者如果是隊尾元素則是NULL
enqueued ReferenceQueue.ENQUEUED 下一個入隊的實例
或者如果是隊尾元素則是this
NULL
inactive ReferenceQueue.NULL this NULL

如何判斷引用狀態(tài)是否是active?
基于以上編碼關(guān)系,垃圾收集器只需要對next字段進(jìn)行判斷罗岖,決定是不是需要進(jìn)行特殊處理涧至。規(guī)則如下:如果next字段為NULL,那么實例是active的桑包;否則南蓬,收集器將會按照普通情況處理。

discovered字段的用途哑了?
當(dāng)引用狀態(tài)為active時赘方,為了保證并發(fā)收集器能夠發(fā)現(xiàn)下一個active引用,同時不影響應(yīng)用線程對這些active引用執(zhí)行enqueue操作弱左,收集器使用了discovered字段記錄了下一個active引用窄陡。
當(dāng)引用狀態(tài)為pending時,discovered字段還記錄了pending列表中的下一個引用拆火。

引用隊列的用途跳夭?
如果程序需要感知對象可達(dá)性的變化時,那么要在創(chuàng)建引用對象時傳入注冊隊列们镜。當(dāng)垃圾收集器發(fā)現(xiàn)referent可達(dá)性發(fā)生變化時币叹,會將referent的引用加入到注冊隊列中。此時模狭,引用處于enqueued狀態(tài)颈抚。程序可以通過阻塞輪詢的方式,從隊列中移除引用胞皱。[2]

已注冊引用和引用注冊隊列的關(guān)系是單向的邪意。也就是說,引用注冊隊列不會跟蹤已注冊引用的狀態(tài)反砌。如果已注冊引用的狀態(tài)變?yōu)椴豢蛇_(dá)雾鬼,那么它永遠(yuǎn)不會進(jìn)入引用注冊隊列。所以宴树,程序需要保證referent對象的可達(dá)性策菜。

如果保證referent的可達(dá)性呢?
一種方式酒贬,使用單獨的線程輪詢又憨,并從隊列中刪除引用對象,然后對其進(jìn)行處理锭吨。
另一種方式蠢莺,在執(zhí)行操作引用時進(jìn)行檢查(lazy)。
例如零如,使用弱引用實現(xiàn)弱鍵的哈希表躏将,可以在每次訪問時輪詢其引用隊列锄弱。這就是WeakHashMap類的工作原理。由于ReferenceQueue.poll方法只檢查內(nèi)部數(shù)據(jù)結(jié)構(gòu)祸憋,因此会宪,將為哈希表訪問方法增加很少的開銷。

引用是何時入隊的?
收集器在將軟引用和弱引用加入注冊隊列(如果有的話)之前蚯窥,自動清除軟引用和弱引用掸鹅。因此,軟引用和弱引用不需要注冊到隊列中才能發(fā)揮作用拦赠,而虛引用則需要注冊隊列巍沙。虛引用對象會保持可達(dá),除非虛引用被清除或者虛引用對象本身不可達(dá)矛紫。

public static void main(String[] args) {
    ReferenceQueue referenceQueue = new ReferenceQueue();
    PhantomReference phantomReference = // 如果不聲明本地變量赎瞎,在gc后將會入隊
            new PhantomReference<>(new Object(), referenceQueue);
    System.gc();
    System.out.println("Object in queue: " + referenceQueue.poll());
}

2 tryHandlePending

處理pending列表中的引用對象。
參考如下代碼颊咬,可見處理過程中,體現(xiàn)了這樣一點牡辽。discovered字段記錄這pending列表中的下一個元素喳篇。

Reference<Object> r;
Cleaner c;
synchronized (lock) {
    if (pending != null) {
        r = pending;
        // 'instanceof' might throw OutOfMemoryError sometimes
        // so do this before un-linking 'r' from the 'pending' chain...
        c = r instanceof Cleaner ? (Cleaner) r : null;
        // unlink 'r' from 'pending' chain
        pending = r.discovered;
        r.discovered = null;
    } else {
        // The waiting on the lock may cause an OutOfMemoryError
        // because it may try to allocate exception objects.
        if (waitForNotify) {
            lock.wait();
        }
        // retry if waited
        return waitForNotify;
    }
}
// Cleaner繼承了PhantomReference,用于一些清理工作
if (c != null) {
    c.clean();
    return true;
}

ReferenceQueue<? super Object> q = r.queue;
// 有注冊隊列态辛,未入隊
if (q != ReferenceQueue.NULL) q.enqueue(r);

3 引用類型

類型 強(qiáng)引用 SoftReference WeakReference PhantomReference
定義 如果線程在不遍歷任何引用對象的情況下訪問某個對象麸澜,則該對象是強(qiáng)可達(dá)的。
新建的對象是線程強(qiáng)可達(dá)的奏黑。
如果對象不是強(qiáng)可達(dá)的炊邦,但可以通過遍歷軟引用來訪問,則該對象是軟可達(dá)的熟史。 如果對象不是強(qiáng)馁害、軟可達(dá)的,但可以通過遍歷弱引用訪問蹂匹,那么它就是弱可達(dá)的碘菜。
當(dāng)對弱可達(dá)對象的弱引用被清除時,就要對該對象執(zhí)行finalization過程限寞。
如果一個對象不是強(qiáng)忍啸、軟、弱可達(dá)的履植,那么它就是幻象可達(dá)的计雌,對象已經(jīng)被終止(finalized),但是幻象引用指向了它玫霎。
用途 普通引用 內(nèi)存敏感的緩存 map 回收預(yù)清理(代替finalization機(jī)制)
垃圾回收 不可達(dá)時 收集器根據(jù)內(nèi)存情況進(jìn)行回收凿滤,保證在OOM之前回收 referent對象狀態(tài)會正常經(jīng)歷finalizable传泊、finalized,進(jìn)而被回收 PhantomReference在收集器確定其referent不需要回收(maybe otherwise be reclaimed)時入隊
強(qiáng)度 依次減弱
是否注冊隊列 - 否(一般) 否(一般)

當(dāng)對象不屬于上述四種可達(dá)方式時鸭巴,成為不可達(dá)對象眷细。不可達(dá)對象,需要進(jìn)行回收鹃祖。

軟引用代碼示例:

public static void main(String[] args) {
    Reference reference = new SoftReference(new Object());
    System.gc(); // 輸出結(jié)果非null
    System.out.println("Object is: " + reference.get());
}

弱引用代碼示例:

public static void main(String[] args) {
    Reference reference = new WeakReference(new Object());
    System.gc(); // 注釋掉時溪椎,輸出結(jié)果非null
    System.out.println("Object is: " + reference.get());
}

幻象引用代碼示例:

public static void main(String[] args) {
    Reference reference = new PhantomReference(new Object(), null);
    System.gc(); // 無論是否注釋掉,結(jié)果始終為null恬口。因為重寫了get方法
    System.out.println("Object is: " + reference.get());
}

問題

參考資料

  1. Reference
  2. package-summary.html#reachability
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末校读,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子祖能,更是在濱河造成了極大的恐慌歉秫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件养铸,死亡現(xiàn)場離奇詭異雁芙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钞螟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門兔甘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳞滨,你說我怎么就攤上這事洞焙。” “怎么了拯啦?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵澡匪,是天一觀的道長。 經(jīng)常有香客問我褒链,道長唁情,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任碱蒙,我火速辦了婚禮荠瘪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赛惩。我一直安慰自己哀墓,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布喷兼。 她就那樣靜靜地躺著篮绰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪季惯。 梳的紋絲不亂的頭發(fā)上吠各,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天臀突,我揣著相機(jī)與錄音,去河邊找鬼贾漏。 笑死候学,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的纵散。 我是一名探鬼主播梳码,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伍掀!你這毒婦竟也來了掰茶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蜜笤,失蹤者是張志新(化名)和其女友劉穎濒蒋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體把兔,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡沪伙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了垛贤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焰坪。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖聘惦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儒恋,我是刑警寧澤善绎,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诫尽,受9級特大地震影響禀酱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牧嫉,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一剂跟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酣藻,春花似錦曹洽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怕轿,卻和暖如春偷崩,著一層夾襖步出監(jiān)牢的瞬間辟拷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工阐斜, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留衫冻,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓谒出,卻偏偏與公主長得像隅俘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子到推,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • ReferenceQueue 引用隊列莉测,在檢測到適當(dāng)?shù)目傻竭_(dá)性更改后颜骤,垃圾回收器將已注冊的引用對象添加到該隊列中 ...
    tomas家的小撥浪鼓閱讀 36,137評論 10 59
  • 引用類型 JDK1.2之后,Java擴(kuò)充了引用的概念捣卤,將引用分為強(qiáng)引用忍抽、軟引用、弱引用和虛引用四種董朝。 強(qiáng)引用類似于...
    德彪閱讀 4,411評論 0 10
  • java.lang.ref 該包下提供了Reference相關(guān)的類鸠项,包括基類Reference,三個子類WeakR...
    chandarlee閱讀 2,251評論 1 50
  • 沒事瞅了幾眼《中國新聲音》子姜,其中有個女孩性格特別放得開祟绊。介紹自己時說導(dǎo)演讓她在人群中,唱歌或者跳舞哥捕,她完全OK牧抽。年...
    佩小姐的奇妙世界閱讀 429評論 1 2
  • 導(dǎo)引:田老師交流去了,李老師來上課啦遥赚!以《歡迎新老師》為題扬舒,寫寫你的所見所感所思,80字左右凫佛。 田老師: ...
    靈動語文閱讀 2,166評論 0 10