[TOC]
## GC回收的是什么?
在應用程序中會使用到兩種內存,分別為堆(Heap)和棧(Stack),GC負責回收堆內存,而不負責回收棧中的內存。
## 為什么不回收棧內存?
棧是一塊專用內存,專門為了函數執行而準備的,存儲著函數中的局部變量以及調用棧。除此以外,棧中的數據都有一個特點——簡單。比如局部變量不能被函數外訪問,所以這塊內存用完就可以直接釋放。正是因為這個特點,棧中的數據可以通過簡單的編譯器指令自動清理,并不需要通過 GC 來回收。
## GC算法種類
* 追蹤式垃圾回收算法
* 引用計數法
## 歷程
GoV1.3- 普通標記清除法,整體過程需要啟動STW,效率極低。
GoV1.5- 三色標記法, 堆空間啟動寫屏障,棧空間不啟動,全部掃描之后,需要重新掃描一次棧(需要STW),效率普通
GoV1.8-三色標記法,混合寫屏障機制, 棧空間不啟動,堆空間啟動。整個過程幾乎不需要STW,效率較高。
### 標記-清除
#### 兩個主要的步驟:
* 標記(Mark phase)
* 清除(Sweep phase)
#### 具體步驟
1. 暫停程序業務邏輯, 分類出可達和不可達的對象,然后做上標記。
2. ,開始標記,程序找出它所有可達的對象
3. 開始清除未標記的對象.
4. 停止暫停,繼續業務
#### 標記-清除(mark and sweep)的缺點
標記清除算法明了,過程鮮明干脆,但是也有非常嚴重的問題。
* STW,stop the world;讓程序暫停,程序出現卡頓**(重要問題)**;
* 標記需要掃描整個heap;
* 清除數據會產生heap碎片。
#### V1.3之前
首先啟動STW暫停,然后執行標記,再執行數據回收,最后停止STW

#### V1.3

將STW的步驟提前了一步,因為在Sweep清除的時候,可以不需要STW停止,因為這些對象已經是不可達對象了,不會出現回收寫沖突等問題
### 三色并發標記法
1. (寫屏障)所有的對象放到白色集合,
2. 遍歷一次根節點,得到灰色節點,
3. 遍歷灰色節點,將可達的對象,從白色標記灰色,遍歷之后的灰色標記成黑色,
4. 由于并發特性,此刻外界向在堆中的對象發生添加對象,以及在棧中的對象添加對象,在堆中的對象會觸發插入屏障機制,棧中的對象不觸發,
5. 由于堆中對象插入屏障,則會把堆中黑色對象添加的白色對象改成灰色,棧中的黑色對象添加的白色對象依然是白色,
6. 循環第 5 步,直到沒有灰色節點,
7. 在準備回收白色前,重新遍歷掃描一次棧空間,加上 STW 暫停保護棧,防止外界干擾(有新的白色會被添加成黑色)在 STW 中,將棧中的對象一次三色標記,直到沒有灰色,
8. 停止 STW,清除白色。至于刪除寫屏障,則是遍歷灰色節點的時候出現可達的節點被刪除,這個時候觸發刪除寫屏障,這個可達的被刪除的節點也是灰色,等循環三色標記之后,直到沒有灰色節點,然后清理白色,刪除寫屏障會造成一個對象即使被刪除了最后一個指向它的指針也依舊可以活過這一輪,在下一輪 GC 中被清理掉。
>相對于普通的標記清除,減少了STW時間,因為標記過程中不需要STW,是和程序并發執行的
因為標記和清除是并發執行的,所以可能在標記的過程中產生了新的引用,就會導致誤清除,所以就加入了寫屏障,直接把新的引用標記成灰色,然后在下一輪掃描中再確認是否刪除
### 混合寫屏障
1、GC開始將棧上的對象全部掃描并標記為黑色(之后不再進行第二次重復掃描,無需STW),
2、GC期間,任何在棧上創建的新對象,均為黑色。
3、被刪除的對象標記為灰色。
4、被添加的對象標記為灰色。
`滿足`: 變形的**弱三色不變式**.
### 三色不變性
* 強三色不變性 — 黑色對象不會指向白色對象,只會指向灰色對象或者黑色對象;
* 弱三色不變性 — 黑色對象指向的白色對象必須包含一條從灰色對象經由多個白色對象的可達路徑
### 插入寫屏障
插入到黑色對象中的白色指針,無論其未來是否會被刪除,屏障都會對其標記為灰色
滿足強三色不變式

### 刪除寫屏障
被刪除的對象,如果自身為灰色或者白色,那么被標記為灰色。
`滿足`:**弱三色不變式**. (保護灰色對象到白色對象的路徑不會斷)
(起始快照的屏障,在對象被接觸引用后,會立即將原引用對象標記為灰色)
### GC 觸發時機
1. 主動觸發:調用 runtime.GC
2. 被動觸發:
* 使用系統監控,該觸發條件由 runtime.forcegcperiod 變量控制,默認為 2 分 鐘。當超過兩分鐘沒有產生任何 GC時,強制觸發 GC。
* 使用步調(Pacing)算法,其核心思想是控制內存增長的比例。如 Go 的 GC 是一種比例 GC, 下一次 GC 結束時的堆大小和上一次 GC 存活堆大小成比例.
### 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 的運行頻率。
### 插入寫屏障詳解
Go GC 在混合寫屏障之前,一直是插入寫屏障,由于棧賦值沒有 hook 的原 因,棧中沒有啟用寫屏障,所以有 STW。Golang 的解決方法是:只是需要在結 束時啟動 STW 來重新掃描棧。這個自然就會導致整個進程的賦值器卡頓
### 刪除寫屏障詳解
一個對象即使被刪除了最后一個指向它的指針也依舊可以 活過這一輪,在下一輪 GC 中才被清理掉。
### 混合寫屏障詳解
1. 混合寫屏障繼承了插入寫屏障的優點,起始無需 STW 打快照,直接并發掃 描垃圾即可;
2. 混合寫屏障繼承了刪除寫屏障的優點,賦值器是黑色賦值器,GC 期間,任 何在棧上創建的新對象,均為黑色。掃描過一次就不需要掃描了,這樣就 消除了插入寫屏障時期最后 STW 的重新掃描棧;
3. 混合寫屏障掃描精度繼承了刪除寫屏障,比插入寫屏障更低,隨著帶來的 是 GC 過程全程無 STW;
4. 混合寫屏障掃描棧雖然沒有 STW,但是掃描某一個具體的棧的時候,還是 要停止這個 goroutine 賦值器的工作(針對一個 goroutine 棧來說,是 暫停掃的,要么全灰,要么全黑哈,原子狀態切換)。
#### **屏障的作用:避免程序運行過程中,變量被誤回收;減少STW的時間**
- 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