垃圾收集器與內存分配策略 -- 如何判斷對象是否存活(1)

背景與目的:在Java與C++之間有一個由內存動態(tài)分配與垃圾回收技術所組成的圍墻聂渊,我們通過學習這圍墻的知識可以更好的解決:出現各種內存溢出,內存泄漏時粟判,當垃圾收集成為系統(tǒng)達到更高并發(fā)量的瓶頸時的問題氏仗。

回收對象:Java內存運行時區(qū)域中的程序計數器,虛擬機棧葱椭,本地方法棧都是跟線程相同的生命周期,在線程結束后口四,內存自然也就隨著回收了。而Java堆與方法區(qū)不一樣秦陋,我們只有在運行時才能知道對象具體分配的內存大小蔓彩,內存的創(chuàng)建于回收都是動態(tài)的,因此垃圾收集器最主要關注的就是這部分的內存驳概。

既然垃圾收集器要將堆中的對象進行回收赤嚼,那就要判斷哪些對象是可以進行回收,哪些是不可以回收的顺又,即判斷哪些是不可能再被任何途徑所使用的對象更卒。

如何判斷對象可以回收

一,引用計數算法(Reference Counting)

算法原理:給對象添加一個引用計數器稚照,每當有一個地方引用它時蹂空,計數器值就添加1,反之減1果录;如果對象的引用計數器值為0上枕,說明該對象不被任何地方引用。

算法優(yōu)點:實現簡單弱恒,判斷效率高辨萍,可以立即回收垃圾(一個對象的引用計數歸零的那一刻即是它成為垃圾的那一刻,同時也是它被回收的那一刻)返弹,沒有暫停時間锈玉。

算法缺點:每次賦值操作的時候都要做相當大的計算爪飘,尤其這里面還有遞歸調用;無法解決對象相互引用的問題拉背,因此在主流的Java虛擬機中并沒有采用這種算法來管理內存悦施。

計數器的值的變化是由mutator引起的,比如以下代碼去团,該例子來源 (引用計數算法 https://zhuanlan.zhihu.com/p/27939756

A objA = new A();
B objB = new B();
objA.ref = objB;

objA與objB的引用分析如圖

objA與objB的引用分析1

objA與objB的引用計數器本來均為1抡诞,但是 objA.ref也引用了objB,因此objB的計數器值為2.
如果我們添加這樣的代碼

objA.ref = null土陪;

那么objA.ref 沒有引用objB昼汗,objB的計數器值就改為為1
那如果說objB也有一個字段引用到了objA呢?

A objA = new A();
B objB = new B();
objA.ref = objB;
objB.ref = objA;

兩個的引用分析圖

objA與objB的引用分析2

那么這兩個對象的引用計數就都是1鬼雀,在使用引用計數算法時就無法將這連個對象回收顷窒,產生循環(huán)引用問題。

二源哩,可達性分析算法(Reachability Analysis)
在Java虛擬機中使用的是這種算法來進行垃圾收集鞋吉。
算法原理:將一系列稱為"GC Roots"的對象作為起始點,從這些節(jié)點開始往下搜索励烦,搜索走過的路徑稱為路徑鏈(Reference Chain)谓着,當一個對象到 GC Roots 沒有任何引用鏈連接,那么說明GC Roots到此對象不可達坛掠,說明此對象不可用赊锚。

比如圖中的Object5,Object6屉栓,Object7 是判定可以回收的對象舷蒲。(圖來源: 周志明的《深入理解Java虛擬機》)

GCRootReachAnalysis.png

在Java語言中,可作為GC Roots對象的由下面幾種:
①虛擬機棧(棧幀中的本地變量表)中引用的對象友多。
②方法區(qū)中的類靜態(tài)屬性引用的對象牲平。
③方法區(qū)中的常量引用對象。
④本地方法棧中JNI(Native方法)引用的對象域滥。

關于GC Roots的正確定義和在 Java可達性分析算法會不會出現循環(huán)引用問題纵柿?https://www.zhihu.com/question/29218534/answer/43580432 中 對 GC Roots有這樣的解釋:

GC Root在對象圖之外,是特別定義的“起點”骗绕,不可能被對象圖內的對象所引用藐窄。
一個常見的誤解是以為GC Root是一組對象。
實際情況是GC Root通常是一組特別管理的指針酬土,這些指針是tracing GC的trace的起點荆忍。它們不是對象圖里的對象,對象也不可能引用到這些“外部”的指針,所以題主想像的情況無法成立刹枉。
另外叽唱,tracing GC能正確處理循環(huán)引用,保證每個活對象只會被訪問一次就能確定其存活性微宝。對象圖里是否存在循環(huán)引用棺亭,tracing GC都能正確判斷對象的存活與否。

相對于引用計數算法蟋软,可達性分析算法的優(yōu)勢是避免了引用計數算法中的循環(huán)引用問題镶摘。

接下來,我們根據這兩種算法岳守,具體分析兩種算法是如何維護所有對象引用的
(參考文章https://www.zhihu.com/question/21539353/answer/95667088

按照我們上面說的凄敢,采用引用計數算法需要在每個實例對象創(chuàng)建之初,通過更改計數器上的引用次數來判斷該對象是否可回收湿痢;而使用可達性分析算法時涝缝,每次GC時,需要遍歷整個GC根節(jié)點來判斷是否回收譬重。

public class Client {
   public static void main(String[] args) {
      GcObject obj1 = new GcObject(); // Step1
      GcObject obj2 = new GcObject(); // Step2
      obj1.instance = obj2; // Step3
      obj2.instance = obj1; //  Step4
      obj1 = null; // Step5
      obj2 = null; // Step6
   }
}
class GcObject{
   public Object instance = null;
}

根據我們上面說明的拒逮,如果采用引用計數算法,最終obj1 與 obj2 兩個實例相互引用臀规,引用計數器上都為1滩援,都不會被GC回收

使用引用計數算法 JVM分析圖

使用引用計數算法 JVM分析圖1

Step1:obj1指向堆中的 GcObject實例1,此時 GcObject實例1的引用計數器值由0變?yōu)?以现;
Step2:obj2指向堆中的 GcObject實例2狠怨,此時 GcObject實例2的引用計數器值由0變?yōu)?;
Step3:實例1中的instance指向 GcObject實例2邑遏, GcObject實例2的引用計數器值由1變?yōu)?;
Step4:實例2中的instance指向 GcObject實例1恰矩, GcObject實例1的引用計數器值由1變?yōu)?记盒;

執(zhí)行到這里,GcObject實例1和GcObject實例2的引用計數器值均為2外傅。

然后執(zhí)行Step5纪吮,Step6

使用引用計數算法 JVM分析圖2

Step5:obj1不再指向堆中的 GcObject實例1, GcObject實例1的引用計數器值由2變成1萎胰;
Step6:obj2不再指向堆中的 GcObject實例2碾盟, GcObject實例2的引用計數器值由2變成1;

到這一步技竟,GcObject實例1和GcObject實例2的引用計數器值均為1冰肴,GC無法回收。但是明顯這兩對象已經沒啥用處,是可以回收的熙尉,所以產生了循環(huán)引用問題联逻,嚴重還會導致內存泄漏的問題。

使用可達性分析算法 JVM分析圖

使用可達性分析算法 JVM分析圖.png

圖中reference1检痰,reference2包归,reference3都是GC Roots,根據路徑鏈(Reference Chain)來判斷:
reference1 ---> 對象實例1
reference2 ---> 對象實例2
reference3 ---> 對象實例4 ---> 對象實例6

明顯對象實例3铅歼,對象實例5之間雖然存在引用公壤,但是均與GC Roots無可達性,所以兩個依舊可以回收的垃圾對象椎椰。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末厦幅,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子俭识,更是在濱河造成了極大的恐慌慨削,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件套媚,死亡現場離奇詭異缚态,居然都是意外死亡,警方通過查閱死者的電腦和手機堤瘤,發(fā)現死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門玫芦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人本辐,你說我怎么就攤上這事桥帆。” “怎么了慎皱?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵老虫,是天一觀的道長。 經常有香客問我茫多,道長祈匙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任天揖,我火速辦了婚禮夺欲,結果婚禮上,老公的妹妹穿的比我還像新娘今膊。我一直安慰自己些阅,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布斑唬。 她就那樣靜靜地躺著市埋,像睡著了一般黎泣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腰素,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天聘裁,我揣著相機與錄音,去河邊找鬼弓千。 笑死衡便,一個胖子當著我的面吹牛,可吹牛的內容都是我干的洋访。 我是一名探鬼主播镣陕,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姻政!你這毒婦竟也來了呆抑?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤汁展,失蹤者是張志新(化名)和其女友劉穎鹊碍,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體食绿,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡侈咕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了器紧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耀销。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铲汪,靈堂內的尸體忽然破棺而出熊尉,到底是詐尸還是另有隱情,我是刑警寧澤掌腰,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布狰住,位于F島的核電站,受9級特大地震影響齿梁,放射性物質發(fā)生泄漏转晰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一士飒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蔗崎,春花似錦酵幕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邓深。三九已至,卻和暖如春笔刹,著一層夾襖步出監(jiān)牢的瞬間芥备,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工舌菜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萌壳,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓日月,卻偏偏與公主長得像袱瓮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子爱咬,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容