Golang Garbage Collector
Go 1.3 mark and sweep方法
步驟:
- 第一步暫停程序業務邏輯,找出所有對象,找出不可達對象,和可達對象
- 第二步開始標記,程序找出它所有可達的對象,並做上標記
- 標記完成,清除未標記的對象
- 停止STW
業界常見的垃圾回收算法有以下幾種:
- 引用計數:對每個對象維護一個引用計數,當引用該對象的對象被銷毀時,引用計數減1,當引用計數器為0時回收該對象。
- 優點:對象可以很快地被回收,不會出現內存耗盡或達到某個閥值時才回收。
- 缺點:不能很好地處理循環引用,而且實時維護引用計數,也有一定的代價。
- 代表語言:Python、PHP、Swift
- 標記-清除:從根變量開始遍歷所有引用的對象,引用的對象標記為”被引用”,沒有被標記的進行回收。
- 優點:解決了引用計數的缺點。
- 缺點:需要STW,即要暫時停掉程序運行。標記需要掃描整個heap。清除數據會產生heap碎片
- 代表語言:Golang(其采用三色標記法)
- 分代收集:按照對象生命周期長短划分不同的代空間,生命周期長的放入老年代,而短的放入新生代,不同代有不同的回收算法和回收頻率。
- 優點:回收性能好
- 缺點:算法復雜
- 代表語言: JAVA
Go1.5 三色標記法
維護三個集合。
白色:所有對象起初全標記為白色,表明還沒有遍歷到的。
GC開始需要做初始化灰色結點,即需要從定義的ROOT集合遍歷找到對應的白色結點,將對應的白色結點轉為灰色結點。
灰色:遍歷第一次得到灰色對象。
黑色:遍歷灰色結點,灰色結點如果仍能找到對象可達則標記為黑色,而下一個節點則標記為灰色。
重復第一步,直到灰色表中沒有任何對象。
收集白色對象(垃圾, 沒有再被任何使用到)
三色標記無STW的問題
三色標記STW性能比較低。
存在的問題:灰色對象2引用的對象3,可能被用戶程序修改,而同時被標記位黑色的對象4又引用了該對象3,因為黑色對象已經不會被掃描,灰色對象2已經不再引用對象3,對象不能被標記為灰色,故最后被GC掉,但是對象4存在引用關系,就會發生錯誤。
強弱三色不變式
強三色不變式 -- 破壞條件1
強制性的不允許黑色對象引用白色對象。
黑色可以引用灰色對象,但是不允許引用白色對象。
弱三色不變式 -- 破壞條件2
黑色可以引用白色對象,白色對象存在其他灰色對象對它的引用(存在另外一條路可達)。
滿足強弱之一,即可保證對象不丟失
插入寫屏障 -- 對象被引用的時候 觸發的機制
A對象引用B對象,B對象被標記為灰色對象。(如果現在是黑色引用白色 就會強制把白色變為黑色 強三色不變式)
棧不啟用插入屏障,堆啟用插入屏障
在准備回收白色之前,需要重新掃描一遍棧空間,加入STW暫停保護棧,防止外界干擾
這樣相當於2遍掃描棧 如果沒有第一次 會發生什么?
刪除屏障 -- 對象被刪除的時候 觸發的機制
被刪除的對象,如果自身為灰色或者白色,那么被標記為灰色(弱三色不變式 保護灰色對象到白色對象的路徑不會斷)
回收精度比較低,每次都可以活過一輪,在下一輪GC中被回收
Go 1.8 三色標記 + 混合寫
具體操作:
- GC開始將棧上的對象全部掃描並標記為黑色(不需要重復掃描以及STW)
- GC期間,任何在棧上創建的對象,均為黑色
- 被刪除的對象標記為灰色
- 被添加的對象標記為灰色
滿足變形的弱三色不變式(結合插入、刪除寫屏障兩者的優點)
1 滿足弱三色
2 滿足強三色
混合寫屏障場景1:對象被一個堆對象刪除引用,成為棧對象的下游
混合場景二:對象被一個棧對象刪除引用,成為另外一個棧對象的下游
混合場景三:對象被一個堆對象刪除引用,成為另外一個堆對象的下游
對象10引用對象7的時候就會將對象7標記為灰色