# 8.11 過去、現在與未來
## 8.14.1 被采納的方案
我們現在來詳細回顧一下 Go 中 GC 在各個版本上的演進歷史。
### Go 1:樸素標記清掃
在 Go 1 的時代,盡管所有的用戶代碼都是并發執行的,但是一旦垃圾回收器開始進行垃圾回收工作時,所有的用戶代碼都會停止執行,而且垃圾回收器僅在一個線程上執行,這時是最原始的垃圾回收器的實現,即單線程版的三色標記清掃。
### Go 1.1, 1.3:并行清掃與精準標記
在 Go 1.3 時候,官方將三色標記清掃算法的垃圾回收代碼改為并行,從而縮短了用戶代碼的停止時間,但是這仍然會造成大量的空隙,如果用戶代碼是一個 Web 應用,且正在處理一個非常重要的請求,則會對請求延遲造成巨大的影響。
**圖 1: Go 1 與 1.3 的垃圾回收器**
### Go 1.5:并發回收
為了解決 STW 問題,官方在 Go 1.5 開始使用 Dijkstra 寫屏障技術讓垃圾回收與用戶代碼得以并行執行。 從而只有在執行寫屏障和很短一段時間內才需要進行 STW。
### Go 1.6:狀態化回收器與位圖技術
TODO:
### Go 1.7:獨立棧收縮
TODO:
### Go 1.8, 1.9:混合寫屏障
Go 團隊在 1.8 引入混合屏障將 STW 進一步縮短,幾乎解決了 STW 的問題。
**圖 1: Go 1.5 與 1.8 的垃圾回收器**
### Go 1.10, 1.11:雙限度機制
### Go 1.12:終止奇點
### Go 1.13:清道夫
### Go 1.14:頁分配器
到了 Go 1.14,由于頁分配器的引入,向操作系統歸還內存的操作頁完全得到并發。
**圖 1: Go 1.13 與 1.14 的垃圾回收器**
## 8.14.2 被拋棄的方案
### 并發棧重掃
早期的 Go 選擇了在 STW 期間,重新對棧進行掃描。 垃圾回收器首先在 GC 循環開始時掃描所有棧從而收集根。 但是如果沒有棧的寫屏障,我們便無法確保堆棧以后不會包含對白色對象的引用, 所以掃描棧只有黑色,直到其 Goroutine 再次執行, 因此它保守地恢復為灰色。從而在循環結束時, 垃圾回收器必須重新掃描灰色堆棧以使其變黑并完成標記任何剩余堆指針。 由于必須保證棧在此期間不會繼續更改,因此重新掃描過程在 STW 時發生。 實踐表明,棧的重掃需要消耗 10 - 100 毫秒的時間。
因為存在重掃的問題,除了引入混合屏障來消除重掃這一過程外, 有另一種做法可以提高重掃過程的性能,那就是將重掃的過程并發執行, 然而這一方案并沒有得以實現。原因很簡單:實現過程相比引入混合屏障而言十分復雜, 而且引入混合屏障能夠消除重掃這一過程,將簡化垃圾回收的步驟。
### ROC
請求制導式回收器可謂成也假設敗也假設。請求制導假設非常符合直覺,但在實現上,由于垃圾回收器必須確保是否有 Goroutine 私有指針被寫入公共對象,因此寫屏障必須一直打開,這也就產生了該方法的致命缺點:昂貴的寫屏障及其帶來的緩存未命中,這也是這一設計最終沒有被采用的主要原因。
### 傳統分代 GC
實踐證明請求制導回收器性能并沒有帶來顯著提升之后,作為備選方案,Go 團隊還嘗試了實現傳統的分代式 GC。但最終同樣發現分代假設并不適用于 Go 的運行棧機制,年輕代對象在棧上就已經死亡,掃描本就該回收的執行棧并沒有為由于分代假設帶來明顯的性能提升,也成為了這一方案最終沒有被采用的主要原因。
## 8.14.3 展望
### 標記輔助時間過長
### 清掃時間過長
### 大規模場景下性能低下
### 對齊導致的內存浪費
### 可能的進化方向
- 第一部分 :基礎篇
- 第1章 Go語言的前世今生
- 1.2 Go語言綜述
- 1.3 順序進程通訊
- 1.4 Plan9匯編語言
- 第2章 程序生命周期
- 2.1 從go命令談起
- 2.2 Go程序編譯流程
- 2.3 Go 程序啟動引導
- 2.4 主Goroutine的生與死
- 第3 章 語言核心
- 3.1 數組.切片與字符串
- 3.2 散列表
- 3.3 函數調用
- 3.4 延遲語句
- 3.5 恐慌與恢復內建函數
- 3.6 通信原語
- 3.7 接口
- 3.8 運行時類型系統
- 3.9 類型別名
- 3.10 進一步閱讀的參考文獻
- 第4章 錯誤
- 4.1 問題的演化
- 4.2 錯誤值檢查
- 4.3 錯誤格式與上下文
- 4.4 錯誤語義
- 4.5 錯誤處理的未來
- 4.6 進一步閱讀的參考文獻
- 第5章 同步模式
- 5.1 共享內存式同步模式
- 5.2 互斥鎖
- 5.3 原子操作
- 5.4 條件變量
- 5.5 同步組
- 5.6 緩存池
- 5.7 并發安全散列表
- 5.8 上下文
- 5.9 內存一致模型
- 5.10 進一步閱讀的文獻參考
- 第二部分 運行時篇
- 第6章 并發調度
- 6.1 隨機調度的基本概念
- 6.2 工作竊取式調度
- 6.3 MPG模型與并發調度單
- 6.4 調度循環
- 6.5 線程管理
- 6.6 信號處理機制
- 6.7 執行棧管理
- 6.8 協作與搶占
- 6.9 系統監控
- 6.10 網絡輪詢器
- 6.11 計時器
- 6.12 非均勻訪存下的調度模型
- 6.13 進一步閱讀的參考文獻
- 第7章 內存分配
- 7.1 設計原則
- 7.2 組件
- 7.3 初始化
- 7.4 大對象分配
- 7.5 小對象分配
- 7.6 微對象分配
- 7.7 頁分配器
- 7.8 內存統計
- 第8章 垃圾回收
- 8.1 垃圾回收的基本想法
- 8.2 寫屏幕技術
- 8.3 調步模型與強弱觸發邊界
- 8.4 掃描標記與標記輔助
- 8.5 免清掃式位圖技術
- 8.6 前進保障與終止檢測
- 8.7 安全點分析
- 8.8 分代假設與代際回收
- 8.9 請求假設與實務制導回收
- 8.10 終結器
- 8.11 過去,現在與未來
- 8.12 垃圾回收統一理論
- 8.13 進一步閱讀的參考文獻
- 第三部分 工具鏈篇
- 第9章 代碼分析
- 9.1 死鎖檢測
- 9.2 競爭檢測
- 9.3 性能追蹤
- 9.4 代碼測試
- 9.5 基準測試
- 9.6 運行時統計量
- 9.7 語言服務協議
- 第10章 依賴管理
- 10.1 依賴管理的難點
- 10.2 語義化版本管理
- 10.3 最小版本選擇算法
- 10.4 Vgo 與dep之爭
- 第12章 泛型
- 12.1 泛型設計的演進
- 12.2 基于合約的泛型
- 12.3 類型檢查技術
- 12.4 泛型的未來
- 12.5 進一步閱讀的的參考文獻
- 第13章 編譯技術
- 13.1 詞法與文法
- 13.2 中間表示
- 13.3 優化器
- 13.4 指針檢查器
- 13.5 逃逸分析
- 13.6 自舉
- 13.7 鏈接器
- 13.8 匯編器
- 13.9 調用規約
- 13.10 cgo與系統調用
- 結束語: Go去向何方?