目前網上有不少不錯的介紹GO語言三色標記GC的文章和源碼分析,這裏推薦一篇我的感受寫的比較不錯的從源碼層面解析GO GC的博客Golang 垃圾回收剖析。看這些文章的過程當中也產生了一些困惑,這裏分享一下我的的思考,若是有不許確的地方歡迎你們批評指正。golang
介紹go gc的文章都會提到,在三色標記的過程當中,從root對象開始遍歷找出全部的活躍對象,但我收集到的資料裏沒有提到什麼是root。那什麼是root對象呢?函數
經過廣度優先遍歷,能夠從root開始找出全部的活躍對象,也包括被間接引用的對象(即被root直接引用的對象中包含的指針所引用的對象)。
p.s. 分配在程序棧上的對象,會隨着函數調用結束棧指針變化而被操做系統回收,不須要GC來處理。源碼分析
被引用
一個活躍的對象包括他所佔用的內存和指向這塊內存的指針,若是程序中存在一個上述的可被追蹤的內存地址指向這塊內存,則對象是活躍的。聲明變量和給變量賦值的時候會改變引用關係。性能
一個對象怎麼從被引用變成不被引用的spa
s := "hello"
s = "bye"
複製代碼
這是因爲在GC進行標記的時候,用戶進程還能夠繼續申請新的對象,若是這個新的對象被一個黑色對象引用,因爲GC不會再去掃描黑色的對象,那麼這個新申請的對象就不會被GC掃描到,而被錯誤的清除。
因此Go語言使用寫屏障的機制(write barrier)來記錄GC標記過程當中對這些新申請對象的引用,並在GC標記的最後階段從新掃描(re-scan)這部分變化的引用關係。
在GC開始的時候有一個短暫的STW來開啓寫屏障,能夠理解成生成一個當前內存狀況的"快照",後面能夠根據這個"快照"判斷哪些變化了的引用關係。在GC標記的最後階段的STW就是來執行這個re-scan去處理write barrier記錄下來的變化,此時若是不STW那麼GC標記就會進入死循環永遠沒法完成。操作系統
這個困惑是在瞭解了Go的內存管理機制後有了答案的。由於Go語言會給對象記錄一個GC generation。GC的並行標記和寫屏障會保證在GC開始前已經存在的對象和標記過程當中新分配的對象具備相同的gc geneartion。而標記結束之後,執行清除的過程當中新產生的對象的GC generation不一樣,GC只會清除具備上次執行標記時的generation的對象。因此以後新產生的對象,在gc清除的時候不管是否被引用,都要等到下一個GC週期纔有可能被清除。線程