深入理解Java中的引用(二)——強軟弱虛引用

深入理解Java中的引用(二)——強軟弱虛引用

在上一篇文章中介紹了Java的Reference類挥萌,本篇文章介紹他的四個子類:強引用蹈垢、軟引用八千、弱引用吗讶、虛引用燎猛。

強引用(StrongReference)

強引用是我們在代碼中最普通的引用恋捆。示例代碼如下:

Object o = new Object();   //  強引用  

在JVM的GC算法中,如果一個對象具有強引用重绷,那么JVM寧可拋出Out of Memory錯誤沸停,垃圾回收器也不會去回收這個對象。
當在代碼里顯示的寫o = null昭卓,或者該對象的引用作用域是在一個函數(shù)里愤钾,代碼如下,當線程調(diào)用完test候醒,就會退出方法棧能颁,引用不存在,垃圾回收器才會在某個時刻回收Object對象倒淫。

public void test(){
    Object o = new Object();   //  強引用 
}

軟引用(SoftReference)

如果一個對象有一個軟引用伙菊,那么在內(nèi)存足夠的情況下,該對象就不會被垃圾回收器回收。網(wǎng)上有很多資料說軟引用只會在內(nèi)存空間不夠用的情況下對象才會被回收镜硕。 那么什么時候才是內(nèi)存不夠用呢运翼?

首先看一下SoftReference類的源碼可以看到有兩個字段。這兩個字段的作用已經(jīng)標注兴枯,這與JVM GC有什么關(guān)系呢血淌?

    /**
     * 記錄最近一次被GC的時間。
     */
    static private long clock;

    /**
     * 每次調(diào)用get方法的時候更新
     * 記錄當前Reference最近一次被訪問的時間
     */
    private long timestamp;

一起看一下HotSpot的源碼财剖,對于軟引用的回收策略見下面should_clear_reference函數(shù)悠夯。

// The oop passed in is the SoftReference object, and not
// the object the SoftReference points to.
bool LRUMaxHeapPolicy::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;
}

上述代碼中interval表示當前引用存活了多久。他的值就是對應上述java代碼中的clocktimestamp相減躺坟。interval_max_interval比較疗疟,如果大于 _max_interval,那么就和弱引用一樣處理瞳氓,如果小于就當做強引用處理策彤。_max_interval的賦值函數(shù)如下:

// Capture state (of-the-VM) information needed to evaluate the policy
void LRUMaxHeapPolicy::setup() {
  size_t max_heap = MaxHeapSize;
  max_heap -= Universe::get_heap_used_at_last_gc();
  max_heap /= M;

  _max_interval = max_heap * SoftRefLRUPolicyMSPerMB;
  assert(_max_interval >= 0,"Sanity check");
}

通過源碼可見首先是max_heap減去上次GC之后剩余堆大小,如果上次GC之后還有很多剩余空間匣摘,說明內(nèi)存空間不夠用了店诗,那么max_heap的值就越小,相應_max_interval也越小音榜,軟引用就越可能被回收庞瘸。

軟引用的一個作用是實現(xiàn)內(nèi)存敏感的高速緩存。比如瀏覽器的后退按鈕赠叼,
(1)如果網(wǎng)頁瀏覽結(jié)束就進行內(nèi)容的回收擦囊,則按后退查看前面瀏覽過的頁面時,需要重新構(gòu)建嘴办。
(2)如果將瀏覽過的網(wǎng)頁存儲到內(nèi)存中會造成內(nèi)存的大量浪費瞬场,甚至會造成內(nèi)存溢出。
通過軟引用可以解決該問題

 Browser prev = new Browser();               // 獲取頁面進行瀏覽  
 SoftReference sr = new SoftReference(prev); // 瀏覽完畢后置為軟引用         
 if(sr.get()!=null){   
    rev = (Browser) sr.get();           // 還沒有被回收器回收涧郊,直接獲取  
 }else{ 
     prev = new Browser();               // 由于內(nèi)存吃緊贯被,所以對軟引用的對象回收了  
     sr = new SoftReference(prev);       // 重新構(gòu)建  
}  

弱引用(WeakReference)

只具有弱引用的對象生命周期更短。當垃圾回收器發(fā)現(xiàn)了只有弱引用的對象時候妆艘,無論內(nèi)存空間是否足夠彤灶,都會被GC回收當你偶爾需要引用某個對象批旺,隨時能獲取該對象幌陕,但是不想介入該對象的生命周期的時候,就可以使用弱引用汽煮, 因為弱引用不會對對象的垃圾回收判斷產(chǎn)生附加的影響搏熄。
當弱引用綁定的對象被垃圾回收的時候茅诱,JVM會把這個弱引用加入到相關(guān)聯(lián)的ReferenceQueue中。
這里拋出兩個問題:
(1)弱引用什么時候會被加入到ReferenceQueue中搬卒,由什么決定的呢瑟俭?
(2)如果綁定的對象GC之后存活了下來,弱引用怎么知道這個對象的新地址呢契邀?
第一種情況摆寄,GC掃描到只存在弱引用的時候就會把它放到鏈表里。還有第二種情況:一個對象既有強引用又有弱引用的情況
下面通過圖片來解釋上面兩個問題:

image.png

上圖表示C同時存在兩個引用:強引用A和弱引用B坯门。

第一種情況:GC先掃描到A
這種情況下GC同時會掃描到C微饥,A和C都會搬到Survivor區(qū)。然后掃描到B古戴,發(fā)現(xiàn)B引用的C搬到到了新的Survivor欠橘,這個時候就把B也搬到Survivor,并把C的新地址更新到B现恼,結(jié)果如下:


image.png

第二種情況:GC先掃描到B
GC還是會先把B放到ReferenceQueue中肃续,由于C還是存活的,所以B會被搬到Survivor中叉袍。然后掃描到A始锚,A和C都會搬到Survivor中,GC結(jié)束的時候B所指向的對象就不對了喳逛,如下圖所示


image.png

該情況下會重新遍歷ReferenceQueue瞧捌,發(fā)現(xiàn)綁定的對象依然存活,C‘ 的指針是指向C的润文,于是就把B再指向C就可以了姐呐,同時因為C依然存活,把B從ReferenceQueue中移除典蝌。新的地址空間如下圖所示:


image.png

具體過程可以查看海納知乎專欄曙砂。

虛引用(PhantomReference)

虛引用不會對對象的垃圾回收有任何附加影響,他與軟引用和弱引用的一個區(qū)別在于:虛引用必須和引用隊列 (ReferenceQueue)聯(lián)合使用赠法。查看他的構(gòu)造方法可以看到必須與一個ReferenceQueue綁定麦轰,而且他的get方法返回的一直是null

public T get() {
        return null;
    }
    
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

虛引用主要用在跟蹤對象垃圾回收的狀態(tài)。具體應用會在在下一節(jié)講到DirectByteBuffer 與ThreadLoal回收的時候詳細分析砖织。

總結(jié)

關(guān)于強引用、軟引用末荐、弱引用與虛引用在垃圾回收時的區(qū)別可以用下圖表示:


image.png

下圖總結(jié)了四種引用在其他方面的區(qū)別:


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侧纯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子甲脏,更是在濱河造成了極大的恐慌眶熬,老刑警劉巖妹笆,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異娜氏,居然都是意外死亡拳缠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門贸弥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窟坐,“玉大人,你說我怎么就攤上這事绵疲≌茉В” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵盔憨,是天一觀的道長徙菠。 經(jīng)常有香客問我,道長郁岩,這世上最難降的妖魔是什么婿奔? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮问慎,結(jié)果婚禮上脸秽,老公的妹妹穿的比我還像新娘。我一直安慰自己蝴乔,他們只是感情好记餐,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薇正,像睡著了一般片酝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挖腰,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天雕沿,我揣著相機與錄音,去河邊找鬼猴仑。 笑死审轮,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的辽俗。 我是一名探鬼主播疾渣,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崖飘!你這毒婦竟也來了榴捡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤朱浴,失蹤者是張志新(化名)和其女友劉穎吊圾,沒想到半個月后达椰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡项乒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年啰劲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檀何。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝇裤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埃碱,到底是詐尸還是另有隱情猖辫,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布砚殿,位于F島的核電站啃憎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏似炎。R本人自食惡果不足惜辛萍,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望羡藐。 院中可真熱鬧贩毕,春花似錦、人聲如沸仆嗦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘩扼。三九已至谆甜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間集绰,已是汗流浹背规辱。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留栽燕,地道東北人罕袋。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像碍岔,于是被迫代替她去往敵國和親浴讯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

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