[TOC]
## **gc 是怎么實現的?**
### **GC機制隨著golang版本變化如何變化的**
GoV1.3- 普通標記清除法,整體過程需要啟動STW,效率極低。
GoV1.5- 三色標記法, 堆空間啟動寫屏障,棧空間不啟動,全部掃描之后,需要重新掃描一次棧(需要STW),效率普通
GoV1.8-三色標記法,混合寫屏障機制, 棧空間不啟動,堆空間啟動。整個過程幾乎不需要STW,效率較高。
### **版本流程**
#### **GoV1.3**
1.3之前:
* STW 暫停
* 執行標記
* 執行數據回收
* 停止 STW
1.3優化:
* STW 暫停
* 執行標記
* 停止 STW
* 執行數據回收
#### **GoV1.5**
1. (寫屏障)所有的對象放到白色集合,
2. 遍歷一次根節點,得到灰色節點,
3. 遍歷灰色節點,將可達的對象,從白色標記灰色,遍歷之后的灰色標記成黑色,
4. 由于并發特性,此刻外界向在堆中的對象發生添加對象,以及在棧中的對象添加對象,在堆中的對象會觸發插入屏障機制,棧中的對象不觸發,
5. 由于堆中對象插入屏障,則會把堆中黑色對象添加的白色對象改成灰色,棧中的黑色對象添加的白色對象依然是白色,
6. 循環第 5 步,直到沒有灰色節點,
7. 在準備回收白色前,重新遍歷掃描一次棧空間,加上 STW 暫停保護棧,防止外界干擾(有新的白色會被添加成黑色)在 STW 中,將棧中的對象一次三色標記,直到沒有灰色,
8. 停止 STW,清除白色。至于刪除寫屏障,則是遍歷灰色節點的時候出現可達的節點被刪除,這個時候觸發刪除寫屏障,這個可達的被刪除的節點也是灰色,等循環三色標記之后,直到沒有灰色節點,然后清理白色,刪除寫屏障會造成一個對象即使被刪除了最后一個指向它的指針也依舊可以活過這一輪,在下一輪 GC 中被清理掉。
#### **GoV1.8**
1、GC開始將棧上的對象全部掃描并標記為黑色(之后不再進行第二次重復掃描,無需STW),
2、GC期間,任何在棧上創建的新對象,均為黑色。
3、被刪除的對象標記為灰色。
4、被添加的對象標記為灰色。
### **插入寫屏障、刪除寫屏障,混合寫屏障**
**屏障的作用:避免程序運行過程中,變量被誤回收;減少STW的時間**
#### **插入寫屏障**
當一個對象引用另外一個對象時,將另外一個對象標記為灰色,以此滿足強三色不變性,不會存在黑色對象引用白色對象。
#### **刪除寫屏障**
在灰色對象刪除對白色對象的引用時,將白色對象置為灰色,其實就是快照保存舊的引用關系,這叫STAB(snapshot-at-the-beginning),以此滿足弱三色不變性。
#### **混合寫屏障**
保證弱三色不變性;該寫屏障會將覆蓋的對象標記成灰色(刪除寫屏障)并在當前棧沒有掃描時將新對象也標記成灰色(插入寫屏障):寫屏障會將被覆蓋的指針和新指針都標記成灰色,而所有新建的對象都會被直接標記成黑色。
## **Go 語言中 GC 的流程是什么?**
Go1.14 版本以 STW 為界限,可以將 GC 劃分為五個階段:
1. GCMark 標記準備階段,為并發標記做準備工作,啟動寫屏障
2. STWGCMark 掃描標記階段,與賦值器并發執行,寫屏障開啟并發
3. GCMarkTermination 標記終止階段,保證一個周期內標記任務完成,停止寫屏 障
4. GCoff 內存清掃階段,將需要回收的內存歸還到堆中,寫屏障關閉
5. GCoff 內存歸還階段,將過多的內存歸還給操作系統,寫屏障關閉。
## **GC 如何調優**
通過 go tool pprof 和 go tool trace 等工具
1. 控制內存分配的速度,限制 Goroutine 的數量,從而提高賦值器對 CPU 的利用率。
2. 減少并復用內存,例如使用 sync.Pool 來復用需要頻繁創建臨時對象,例 如提前分配足夠的內存來降低多余的拷貝。
3. 需要時,增大 GOGC 的值,降低 GC 的運行頻率。
## **GC 觸發時機**
1. 主動觸發:調用 runtime.GC
2. 被動觸發:
* 使用系統監控,該觸發條件由 runtime.forcegcperiod 變量控制,默認為 2 分 鐘。當超過兩分鐘沒有產生任何 GC時,強制觸發 GC。
* 使用步調(Pacing)算法,其核心思想是控制內存增長的比例。如 Go 的 GC 是一種比例 GC, 下一次 GC 結束時的堆大小和上一次 GC 存活堆大小成比例.
## **GC 中 stw 時機,各個階段是如何解決的?**
1)在開始新的一輪 GC 周期前,需要調用 gcWaitOnMark 方法上一輪 GC 的標記結束(含掃描終止、標記、或標記終止等)。
2)開始新的一輪 GC 周期,調用 gcStart 方法觸發 GC 行為,開始掃描標記階段。
3)需要調用 gcWaitOnMark 方法等待,直到當前 GC 周期的掃描、標記、標記終止完成。
4)需要調用 sweepone 方法,掃描未掃除的堆跨度,并持續掃除,保證清理完成。在等待掃除完畢前的阻塞時間,會調用 Gosched 讓出。
5)在本輪 GC 已經基本完成后,會調用 mProf\_PostSweep 方法。以此記錄最后一次標記終止時的堆配置文件快照。
6)結束,釋放 M。
- Go準備工作
- 依賴管理
- Go基礎
- 1、變量和常量
- 2、基本數據類型
- 3、運算符
- 4、流程控制
- 5、數組
- 數組聲明和初始化
- 遍歷
- 數組是值類型
- 6、切片
- 定義
- slice其他內容
- 7、map
- 8、函數
- 函數基礎
- 函數進階
- 9、指針
- 10、結構體
- 類型別名和自定義類型
- 結構體
- 11、接口
- 12、反射
- 13、并發
- 14、網絡編程
- 15、單元測試
- Go常用庫/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go優化
- Go問題排查
- Go框架
- 基礎知識點的思考
- 面試題
- 八股文
- 操作系統
- 整理一份資料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小點
- 樹
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面試題
- 基礎
- Map
- Chan
- GC
- GMP
- 并發
- 內存
- 算法
- docker