垃圾回收算法概要

##標記-清除法
問世最早的GC算法,分爲標記清除2個階段.

  • 標記
    根對象開始遞歸標記所有能訪問到的對象,這些對象就是活動的.
    遞歸過程可能是DFS或BFS

  • 清除
    首地址開始,遍歷每個對象的標誌位,如果不是活動的,就回收對象,具體表現是鏈接到空閒鏈表.下一次再分配時,直接從空閒鏈表裏取.

    清除階段,堆越大,耗時越長,最大暫停時間就越大.有延遲清除法可以緩解最大暫停時間問題,每次只清除上一次截止位置的右側部分.

  • 優點
    實現簡單,與保守式GC算法兼容.(因爲清除過程不移動對象,僅僅是鏈接到空閒鏈表,下次重複使用)
    保守式GC算法不移動對象.

  • 缺點
    碎片化:會逐步產生被細化的分塊,長時間運行後,一大堆小對象分散在堆的各處.
    分配速度較慢:因爲分配對象時,需要遍歷空閒鏈表,尋找合適的塊,最差情況遍歷到表尾.

  • 當然可以採用多個空閒鏈表,把大塊和小塊的對象,放在不同的空閒連表裏.但是這個方案只能緩解,不能根除分配慢.

  • 還可以Big Bag Of Pages:將大小接近的對象,整理成爲固定大小的塊,統一管理.但也不能根除碎片化.

##引用計數法
每個對象有個計數器,記錄着當前被多少人引用,若計數器爲0,表示不再被需要,回收.

  • 優點
    可立即回收:計數減少到0,立即感知
    最大暫停時間小:不需要遍歷整個堆,時間取決於程序當前時刻引用爲0對象的數量

  • 缺點
    計數是個苦逼活
    計數器的值可能很大,佔空間:比如32位機器,最多可以2的32次方個對象引用同一個對象,所以計數器必須至少佔32位.
    循環引用怎麼搞?默認無法回收,想回收,就得單獨拎出來討論

  • 改進點

  • 延遲引用計數:打個栗子:某個對象引用高頻變化,每秒上千次,能不能先緩存着,一秒更新一次計數值.更具體一點,某個對象在很短時間內,引用暴增10000,計數值能不能不要自增一萬次,而是一次更新,直接加上1萬?
    延遲引用法,弊端自然是垃圾不能立即回收,回收有延遲

  • sticky計數法: 計數值不是浪費內存嘛?這個主意就是想,大部分對象引用數都很小,別尼瑪浪費32個位好麼?他的主意就是,比如用5個位來計數,計數值太大,溢出了,你就用標記清除法吧!
    事實這個主意是不錯的,基本上絕大多數對象的引用數都不大.

##GC複製法
簡單點說,堆分成from和to兩部分,程序初始使用的是from,垃圾回收時,把from裏面活動的對象直接拷貝到to,再把from和to指針互換.下一次,又可以繼續從from拷貝活動的對象去to裏面.

  • 優點
    優秀的吞吐量:只需要標記活動對象,然後搜索整堆,拷貝活的,比標記清除法省事多了

    高速分配: 每次拷貝過去後,又是一塊連續的內存空間,只要用一個指針$free指向空閒空間的起點,每次分配後,後移指針$free即可.O(1)時空複雜度.

    無碎片化: 每次拷貝時都會緊湊安排對象位置,從to部分起始地址順序使用,無碎片化一說

    與緩存兼容: 拷貝過去的活對象,有引用關係的會被放在相鄰位置.很好理解嘛,拷貝活動對象A,你總要把A引用(或者說)依賴的B,C…一起拷貝過去吧?然後又是順序依次使用內存地址,那麼A,B,C肯定坐在一起咯

  • 缺點
    堆使用效率低下:這個傻子都能想到吧? 你每次只能使用一半的空間.

    不兼容保守式GC算法:這個傻子也能想到吧? 你很明顯移動對象了啊

    遞歸拷貝依賴的對象:上面的栗子,拷貝A,需要遞歸拷貝A的依賴B,C,拷貝B的時候又要遞歸拷貝B的依賴D…

  • 改進點
    多空間複製算法:剛纔不是說了麼,堆空間一分爲二,from和to.使用率50%.

    咋能不能這樣:堆空間分成10等份,from0,from1…from7和from和to,看到這裏,你已經明白了.from和to這2份空間用於執行GC複製算法,剩下的8份就GC標記清除法.

    每搞完一輪,就更換一次from.比如剛開始是from和to採用GC複製法,下一輪就是from0和to採用GC複製法,再下一輪就是from1…說白了就是那一堆from輪着和to做GC複製法,沒有輪上的就標記清除法.

    這樣堆的使用率是不是提高了一些?

##分代垃圾回收

  • 經驗法則:大部分對象短命,只有少部分對象長壽.在堆上設置生成空間,2個大小相等的倖存空間(from,to),老年空間4塊.

  • 新生代GC: 生成空間和from倖存–複製到–>to倖存; from和to交替;新生代基本是複製算法,每次複製活着的對象去to

  • 新生代GC會使大多數對象被回收, 但有少數對象能存活下來, 我們稱爲晉升.

晉升的過程依賴一個叫做"記錄集"的東西, 這個東西作用就是新生代的A,晉升稱爲老年代的A1後, 用於更新所有引用A的對象,告訴他們,A變了,變老了,變成A1了

  • 老年代GC: 老年代由於空閒比整體堆小很多, 不適合複製算法, 往往是標記-清除法.老年代GC很費時間, 所以沒法縮短最大暫停時間.

爲了緩解老年代GC的長時間暫停, 提出了列車垃圾回收, 較爲複雜.

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