JVM那些事兒(三)-----常見垃圾回收算法

一,如何判斷對象是可回收的對象?

通過可達(dá)性分析(Reachability Analysis)來判定對象是否存活的.
這個算法的基本思路是通過一些列的稱為"GC Roots"的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個對象到GC Roots沒有任何引用鏈相連接時,則證明此對象是不可用的.

Java語言中,可作為GC Roots的對象主要包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法區(qū)中常量引用的對象
  • 本地方法棧中JNI(即一般說的Native方法)引用的對象.
二,如何判斷對象是死亡的?

即使在可達(dá)性分析算法中不可達(dá)的對象,也并非是“非死不可”的圆凰,這時候它們暫時處于“緩刑”階段忆家,要真正宣告一個對象死亡,至少要經(jīng)歷兩次標(biāo)記過程:如果對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈卷哩,那它將會被第一次標(biāo)記并且進(jìn)行一次篩選昂灵,篩選的條件是此對象是否有必要執(zhí)行finalize()方法色鸳。當(dāng)對象沒有覆蓋finalize()方法琉历,或者finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過,虛擬機(jī)將這兩種情況都視為“沒有必要執(zhí)行”水醋。

如果這個對象被判定為有必要執(zhí)行finalize()方法旗笔,那么這個對象將會放置在一個叫做F-Queue的隊(duì)列之中,并在稍后由一個由虛擬機(jī)自動建立的拄踪、低優(yōu)先級的Finalizer線程去執(zhí)行它蝇恶。這里所謂的“執(zhí)行”是指虛擬機(jī)會觸發(fā)這個方法,但并不承諾會等待它運(yùn)行結(jié)束惶桐,這樣做的原因是撮弧,如果一個對象在finalize()方法中執(zhí)行緩慢,或者發(fā)生了死循環(huán)(更極端的情況)姚糊,將很可能會導(dǎo)致F-Queue隊(duì)列中其他對象永久處于等待贿衍,甚至導(dǎo)致整個內(nèi)存回收系統(tǒng)崩潰。finalize()方法是對象逃脫死亡命運(yùn)的最后一次機(jī)會救恨,稍后GC將對F-Queue中的對象進(jìn)行第二次小規(guī)模的標(biāo)記贸辈,如果對象要在finalize()中成功拯救自己——只要重新與引用鏈上的任何一個對象建立關(guān)聯(lián)即可,譬如把自己(this關(guān)鍵字)賦值給某個類變量或者對象的成員變量肠槽,那在第二次標(biāo)記時它將被移除出“即將回收”的集合擎淤;如果對象這時候還沒有逃脫,那基本上它就真的被回收了秸仙。

示例代碼如下:

public class FinalizeEscapeGc{
    public static FinalizeEscapeGc SAVE_HOOK = null;

    public  void isAlive(){
        System.out.println("yes, i am still alive:");
    }

    protected void finalize() throws Throwable{
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGc.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws Throwable{
        SAVE_HOOK = new FinalizeEscapeGc();
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(5000);
        //第一次逃脫
        if(SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("no, i am dead:");
        }
    
      //第二次沒有逃脫
        SAVE_HOOK = null;
        System.gc();
        if(SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("no i am dead-------------");
        }

    }  
}

備注:finalize()介紹

  • finalize()作為根類Object的受保護(hù)方法.
  • 當(dāng)垃圾回收器確定不存在對該對象的更多引用時嘴拢,由對象的垃圾回收器調(diào)用此方法。子類重寫 finalize 方法寂纪,以配置系統(tǒng)資源或執(zhí)行其他清除.
  • 對于任何給定對象席吴,Java 虛擬機(jī)最多只調(diào)用一次 finalize 方法
三,垃圾回收算法
1,標(biāo)記--清除算法(Mark-Sweep)
  • A,算法思路
    該回收算法分為兩個階段 標(biāo)記清除

首先標(biāo)記處所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象.標(biāo)記--清除是最基礎(chǔ)的收集算法,后續(xù)的收集算法都是基于這種思路并對其不足進(jìn)行改進(jìn)而得到的

  • B,算法的不足
    • 效率問題
      標(biāo)記和清除的效率都不高
    • 空間問題
      標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后在程序運(yùn)行過程中需要分配較大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作.
2,復(fù)制算法
  • A,算法思路

該算法是將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中一塊.當(dāng)這塊內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉.每次只對整個半?yún)^(qū)進(jìn)行內(nèi)存回收

  • B,算法優(yōu)缺點(diǎn)

    • 內(nèi)存分配不必考慮內(nèi)存碎片問題
    • 內(nèi)存分配簡單高效
    • 但是將可使用的內(nèi)存減少到了原來的一半
  • C,算法的應(yīng)用

    • 在商業(yè)虛擬機(jī)中,都采用這種收集算法來回收新生代.新生代的大部分內(nèi)存都是"朝生夕死"的,所以沒必要按照1:1的比例來劃分內(nèi)存空間,而是將內(nèi)存分為一塊交大 的Eden(伊甸園)空間和兩塊較小的Survivor(from survivor, to survivor),每次使用Eden和from Survivor空間. 當(dāng)回收時, 將Eden和from Survivor中還存活著的對象一次性地復(fù)制到另外to Survivor空間上,最后清理掉Eden和from Survivor空間.
    • 但若出現(xiàn)Eden和from Survivor中存活的對象大于to Survivor怎么辦呢?這就需要依賴其他內(nèi)存進(jìn)行(例如老年代)進(jìn)行分配擔(dān)保(Handle Promotion). 當(dāng)to Survivor空間沒有足夠空間存放新生代收集下來的存活對象時,這些對象將直接通過分配擔(dān)保機(jī)制進(jìn)入老年代.
3,標(biāo)記---整理(Mark-Compact)
  • A,算法思路

該算法的標(biāo)記過程仍然與"標(biāo)記---清除"算法一樣, 但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都想一端移動,然后直接清理掉端邊界以外的內(nèi)存.
-B,算法應(yīng)用
主要應(yīng)用老年代垃圾回收

4,分代收集算法

當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用"分代收集"算法,主要根據(jù)對象存活周期的不同內(nèi)存劃分為幾塊.一般把堆分為新生代和老年代,根據(jù)不同年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴?
在新生代中,由于每次垃圾收集時都會有大批對象死去,只有少量存活,那就選用復(fù)制算法.
在老年代中因?yàn)閷ο蟠婊钚矢?沒有額外空間對它進(jìn)行分配擔(dān)保,就必須使用"標(biāo)記---清理"或者"標(biāo)記---整理"來進(jìn)行回收

參考<<深入理解Java虛擬機(jī) JVM高級特性與最佳實(shí)踐 第二版 周志明>>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弊攘,隨后出現(xiàn)的幾起案子抢腐,更是在濱河造成了極大的恐慌,老刑警劉巖襟交,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迈倍,死亡現(xiàn)場離奇詭異,居然都是意外死亡捣域,警方通過查閱死者的電腦和手機(jī)啼染,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門宴合,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迹鹅,你說我怎么就攤上這事卦洽。” “怎么了斜棚?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵阀蒂,是天一觀的道長。 經(jīng)常有香客問我弟蚀,道長蚤霞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任义钉,我火速辦了婚禮昧绣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捶闸。我一直安慰自己夜畴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布删壮。 她就那樣靜靜地躺著贪绘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醉锅。 梳的紋絲不亂的頭發(fā)上兔簇,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音硬耍,去河邊找鬼垄琐。 笑死,一個胖子當(dāng)著我的面吹牛经柴,可吹牛的內(nèi)容都是我干的狸窘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坯认,長吁一口氣:“原來是場噩夢啊……” “哼翻擒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起牛哺,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陋气,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后引润,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巩趁,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年淳附,在試婚紗的時候發(fā)現(xiàn)自己被綠了议慰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蠢古。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖别凹,靈堂內(nèi)的尸體忽然破棺而出草讶,到底是詐尸還是另有隱情,我是刑警寧澤炉菲,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布堕战,位于F島的核電站,受9級特大地震影響拍霜,放射性物質(zhì)發(fā)生泄漏践啄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一沉御、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昭灵,春花似錦吠裆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抠蚣,卻和暖如春祝旷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嘶窄。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工怀跛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柄冲。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓吻谋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親现横。 傳聞我的和親對象是個殘疾皇子漓拾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

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