垃圾回收(GC)的三種基本方式

垃圾回收(GC)的三種基本方式



垃圾(Garbage)就是程序需要回收的對象,如果一個對象不在被直接或間接地引用,那麼這個對象就成爲了「垃圾」,它佔用的內存需要及時地釋放,否則就會引起「內存泄露」。有些語言需要程序員來手動釋放內存(回收垃圾),有些語言有垃圾回收機制(GC)。本文就來討論GC實現的三種基本方式。

其實這三種方式也可以大體歸爲兩類:跟蹤回收,引用計數。美國IBM的沃森研究中心David F.Bacon等人發佈的「垃圾回收統一理論」一文闡述了一個理論:任何垃圾回收的思路,無非以上兩種的組合,其中一種的改善和進步,必然伴隨着另一種的改善和進步。

跟蹤回收

跟蹤回收的方式獨立於程序,定期運行來檢查垃圾,需要較長時間的中斷。

標記清除

標記清除的方式需要對程序的對象進行兩次掃描,第一次從根(Root)開始掃描,被根引用了的對象標記爲不是垃圾,不是垃圾的對象引用的對象同樣標記爲不是垃圾,以此遞歸。所有不是垃圾的對象的引用都掃描完了之後。就進行第二次掃描,第一次掃描中沒有得到標記的對象就是垃圾了,對此進行回收。

複製收集

複製收集的方式只需要對對象進行一次掃描。準備一個「新的空間」,從根開始,對對象進行掃,如果存在對這個對象的引用,就把它複製到「新空間中」。一次掃描結束之後,所有存在於「新空間」的對象就是所有的非垃圾對象。

這兩種方式各有千秋,標記清除的方式節省內存但是兩次掃描需要更多的時間,對於垃圾比例較小的情況佔優勢。複製收集更快速但是需要額外開闢一塊用來複制的內存,對垃圾比例較大的情況佔優勢。特別的,複製收集有「局部性」的優點。

在複製收集的過程中,會按照對象被引用的順序將對象複製到新空間中。於是,關係較近的對象被放在距離較近的內存空間的可能性會提高,這叫做局部性。局部性高的情況下,內存緩存會更有效地運作,程序的性能會提高。

對於標記清除,有一種標記-壓縮算法的衍生算法:

對於壓縮階段,它的工作就是移動所有的可達對象到堆內存的同一個區域中,使他們緊湊的排列在一起,從而將所有非可達對象釋放出來的空閒內存都集中在一起,通過這樣的方式來達到減少內存碎片的目的。

引用計數

引用計數是指,針對每一個對象,保存一個對該對象的引用計數,該對象的引用增加,則相應的引用計數增加。如果對象的引用計數爲零,則回收該對象。

優點:引用計數最大的優點就是容易實現,C++程序員應該都實現過類似的機制。二是成本小,基本上引用計數爲0的時候垃圾會被立即回收,而其他方法難以預測對象的生命週期,垃圾存在的時間都會比這個方法高。另,這種垃圾回收方式產生的中斷時間最短。

缺點:最著名的缺點就是如果對象中存在循環引用,就無法被回收。例如,下面三個對象互相引用,但是不存在從根(Root)指向的引用,所以已經是垃圾了。但是引用計數不爲0.

還有一個缺點就是,引用計數不適合在並行中使用,多個線程同時操作引用計數,會引起數值不一樣的問題從而導致內存錯誤。所以引用計數必須採用獨佔方式,如果引用操作頻繁,那麼加鎖等併發控制機制的開銷是相當大的。

Perl和Python採用了這種GC機制。

它們的衍生算法

分代回收

這種回收方式用了程序的一種特性:大部分對象會從產生開始在很短的時間內變成垃圾,而存在的很長時間的對象往往都有較長的生命週期。高頻對新生成的對象進行回收,稱爲「小回收」,低頻對所有對象回收,稱爲「大回收」。每一次「小回收」過後,就把存活下來的對象歸爲「老生代」,「小回收」的時候,遇到老生代直接跳過。大多數分代回收算法都採用的「複製收集」方法,因爲小回收中垃圾的比例較大。

這種方式存在一個問題:如果在某個新生代的對象中,存在「老生代」的對象對它的引用,它就不是垃圾了,那麼怎麼制止「小回收」對其回收呢?這裏用到了一中叫做寫屏障的方式。

程序對所有涉及修改對象內容的地方進行保護,被稱爲「寫屏障」(Write Barrier)。寫屏障不僅用於分代回收,也用於其他GC算法中。

在此算法的表現是,用一個記錄集來記錄從新生代到老生代的引用。如果有兩個對象A和B,當對A的對象內容進行修改並加入B的引用時,如果①A是「老生代」②B是「新生代」。則將這個引用加入到記錄集中。「小回收」的時候,因爲記錄集中有對B的引用,所以B不再是垃圾。

增量回收

上面的算法縮短了「GC平均中斷時間」,但是在對實時性要求很高的程序中,對「GC最高中斷時間」的要求更高。比如,自動駕駛軟件,如果某次GC中斷了0.1s,那麼損失可能是致命的。

增量回收就是將GC分成幾部分來執行。設置「GC最多中斷10ms」這樣的條件限制來使GC的終端時間視作可預測的。

但是,在兩段的GC程序之間,引用關係可能發生了變化。所以,這種GC算法也要寫屏障,來記錄引用關係的變化。雖然這種方式控制了中斷最高時間,但是由於中斷次數增加,GC總時間是增加的。

並行回收

基本原理是,在程序運行的同時進行GC工作,最大化CPU的性能。但是這種方式也要面對增量回收的問題,所以也要進行寫屏障操作。

然而這種方式也並未做到完全不暫停原程序的運行,在某些特定的GC階段還是要暫停原程序。多核化迅速發展的今天,這種算法也在不斷優化。不間斷原程序實現並行回收這個領域是相當值得期待的。


原文地址http://blog.jobbole.com/91960/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章