標記-清除算法

前言

垃圾自動回收機制的出現(xiàn)使編程更加的簡單庶橱,使得我們不需要再去考慮內(nèi)存分配和釋放的問題睁蕾,而是更加的專注在我們產(chǎn)品功能的實現(xiàn)上蛛倦。但是我們還是需要花時間去了解下垃圾收集機制是怎么工作的年柠,以便后面能夠更好的進行我們應(yīng)用的性能調(diào)優(yōu)等扰楼。

目前最基本的垃圾收集算法有四種,標記-清除算法(mark-sweep),標記-壓縮算法(mark-compact),復(fù)制算法(copying)以及引用計數(shù)算法(reference counting).而現(xiàn)代流行的垃圾收集算法一般是由這四種中的其中幾種算法相互組合而成,比如說划鸽,對堆(heap)的一部分采用標記-清除算法输莺,對堆(heap)的另外一部分則采用復(fù)制算法等等戚哎。今天我們主要來看下標記-清除算法的原理。

基本概念

在了解標記-清除算法前嫂用,我們先要了解幾個基本概念型凳。

首先是mutator和collector,這兩個名詞經(jīng)常在垃圾收集算法中出現(xiàn)嘱函,collector指的就是垃圾收集器啰脚,而mutator是指除了垃圾收集器之外的部分,比如說我們應(yīng)用程序本身实夹。mutator的職責(zé)一般是NEW(分配內(nèi)存),READ(從內(nèi)存中讀取內(nèi)容),WRITE(將內(nèi)容寫入內(nèi)存)橄浓,而collector則就是回收不再使用的內(nèi)存來供mutator進行NEW操作的使用。

第二個基本概念是關(guān)于mutator roots(mutator根對象),mutator根對象一般指的是分配在堆內(nèi)存之外亮航,可以直接被mutator直接訪問到的對象荸实,一般是指靜態(tài)/全局變量以及Thread-Local變量(在Java中,存儲在java.lang.ThreadLocal中的變量和分配在棧上的變量 - 方法內(nèi)部的臨時變量等都屬于此類).

第三個基本概念是關(guān)于可達對象的定義缴淋,從mutator根對象開始進行遍歷准给,可以被訪問到的對象都稱為是可達對象。這些對象也是mutator(你的應(yīng)用程序)正在使用的對象重抖。

算法原理

顧名思義露氮,標記-清除算法分為兩個階段,標記(mark)和清除(sweep).

在標記階段钟沛,collector從mutator根對象開始進行遍歷畔规,對從mutator根對象可以訪問到的對象都打上一個標識,一般是在對象的header中恨统,將其記錄為可達對象叁扫。

而在清除階段,collector對堆內(nèi)存(heap memory)從頭到尾進行線性的遍歷畜埋,如果發(fā)現(xiàn)某個對象沒有標記為可達對象-通過讀取對象的header信息莫绣,則就將其回收。

從上圖我們可以看到悠鞍,在Mark階段对室,從根對象1可以訪問到B對象,從B對象又可以訪問到E對象咖祭,所以B,E對象都是可達的掩宜。同理,F(xiàn),G,J,K也都是可達對象心肪。到了Sweep階段锭亏,所有非可達對象都會被collector回收纠吴。同時硬鞍,Collector在進行標記和清除階段時會將整個應(yīng)用程序暫停(mutator),等待標記清除結(jié)束后才會恢復(fù)應(yīng)用程序的運行,這也是Stop-The-World這個單詞的來歷固该。

接著我們先看下一般垃圾收集動作是怎么被觸發(fā)的锅减,下面是mutator進行NEW操作的偽代碼:

New():
    ref <- allocate()  //分配新的內(nèi)存到ref指針
    if ref == null
       collect()  //內(nèi)存不足,則觸發(fā)垃圾收集
       ref <- allocate()
       if ref == null
          throw "Out of Memory"   //垃圾收集后仍然內(nèi)存不足伐坏,則拋出Out of Memory錯誤
          return ref

atomic collect():
    markFromRoots()
    sweep(HeapStart,HeapEnd)

而下面是對應(yīng)的mark算法:

markFromRoots():
    worklist <- empty
    for each fld in Roots  //遍歷所有mutator根對象
        ref <- *fld
        if ref != null && isNotMarked(ref)  //如果它是可達的而且沒有被標記的怔匣,直接標記該對象并將其加到worklist中
           setMarked(ref)
           add(worklist,ref)
           mark()
mark():
    while not isEmpty(worklist)
          ref <- remove(worklist)  //將worklist的最后一個元素彈出,賦值給ref
          for each fld in Pointers(ref)  //遍歷ref對象的所有指針域桦沉,如果其指針域(child)是可達的每瞒,直接標記其為可達對象并且將其加入worklist中
          //通過這樣的方式來實現(xiàn)深度遍歷,直到將該對象下面所有可以訪問到的對象都標記為可達對象纯露。
                child <- *fld
                if child != null && isNotMarked(child)
                   setMarked(child)
                   add(worklist,child)

在mark階段結(jié)束后剿骨,sweep算法就比較簡單了,它就是從堆內(nèi)存起始位置開始埠褪,線性遍歷所有對象直到堆內(nèi)存末尾,如果該對象是可達對象的(在mark階段被標記過的),那就直接去除標記位(為下一次的mark做準備)顷啼,如果該對象是不可達的藕夫,直接釋放內(nèi)存。

sweep(start,end):
    scan <- start
   while scan < end
       if isMarked(scan)
          setUnMarked(scan)
      else
          free(scan)
      scan <- nextObject(scan)

缺點

標記-清除算法的比較大的缺點就是垃圾收集后有可能會造成大量的內(nèi)存碎片渴语,像上面的圖片所示苹威,垃圾收集后內(nèi)存中存在三個內(nèi)存碎片,假設(shè)一個方格代表1個單位的內(nèi)存驾凶,如果有一個對象需要占用3個內(nèi)存單位的話屠升,那么就會導(dǎo)致Mutator一直處于暫停狀態(tài),而Collector一直在嘗試進行垃圾收集狭郑,直到Out of Memory腹暖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市翰萨,隨后出現(xiàn)的幾起案子脏答,更是在濱河造成了極大的恐慌,老刑警劉巖亩鬼,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殖告,死亡現(xiàn)場離奇詭異,居然都是意外死亡雳锋,警方通過查閱死者的電腦和手機黄绩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玷过,“玉大人爽丹,你說我怎么就攤上這事筑煮。” “怎么了粤蝎?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵真仲,是天一觀的道長。 經(jīng)常有香客問我初澎,道長秸应,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任碑宴,我火速辦了婚禮软啼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘延柠。我一直安慰自己焰宣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布捕仔。 她就那樣靜靜地躺著匕积,像睡著了一般。 火紅的嫁衣襯著肌膚如雪榜跌。 梳的紋絲不亂的頭發(fā)上闪唆,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音钓葫,去河邊找鬼悄蕾。 笑死,一個胖子當(dāng)著我的面吹牛础浮,可吹牛的內(nèi)容都是我干的帆调。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼豆同,長吁一口氣:“原來是場噩夢啊……” “哼番刊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起影锈,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芹务,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸭廷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枣抱,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年辆床,在試婚紗的時候發(fā)現(xiàn)自己被綠了佳晶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡讼载,死狀恐怖轿秧,靈堂內(nèi)的尸體忽然破棺而出中跌,到底是詐尸還是另有隱情,我是刑警寧澤淤刃,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吱型,受9級特大地震影響逸贾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜津滞,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一铝侵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧触徐,春花似錦咪鲜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鸟雏,卻和暖如春享郊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孝鹊。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工炊琉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人又活。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓苔咪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柳骄。 傳聞我的和親對象是個殘疾皇子团赏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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