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


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

  垃圾:就是程序需要回收的對象,如果一個對象不在被直接或者間接地引用,那么這個對象就成為了垃圾,它占用的內存需要及時地釋放,否則就會引起內存泄漏。

  這里可以大致的分為兩類:跟蹤回收,引用計數。

  垃圾回收統一理論一文闡述了一個理論:任何垃圾回收的思路,無非以上兩種的組合,其中一種的改善和進步,必然伴隨着另一種的改善和進步。

跟蹤回收:

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

標記清除:

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

 

 

 復制收集

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

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

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

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

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

引用計數

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

優點:

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

缺點:

  • 最著名的缺點就是如果對象中存在循環引用,就無法被回收。例如:下面三個對象互相引用,但是不存在從根(Root)指向的引用,所以已經是垃圾了。但是引用計數不為0.
  • 引用計數不適合在並行中使用,多個線程同時操作引用計數,會引起數值不一樣的問題,從而導致內存錯誤。所以引用計數必須采用獨占方式,如果引用操作頻繁,那么加鎖等並發控制機制的開銷是相當大的。

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

他們的衍生算法

分代回收:

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

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

程序對所有涉及修改對象內容的地方進行保護,被稱為寫屏障(writer 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階段還是要暫定源程序。多核化迅速發展的今天,這種蘇昂法也在不斷優化。不間斷原程序實現並行回收這個領域是相當值得期待的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM