一、 使用 sync.Pool 減少 GC 壓力,提升性能
簡單講下go的gc,它的核心原理就是三色標記法和寫屏障,可以實現優秀并發處理。gc一般不會頻繁調用,他是根據GOGC的值來判斷,具體就是上次觸發GC后總堆值大于等于上次的(1+GOGC/100)倍,就會觸發gc。所以為了不出現gc頻繁調用損耗性能,一般會采用:增大GOGC值(得測試不能隨便調);盡量減少堆的產生:1.使用棧變量 2. 使用sync.pool 對象池 的辦法,這里講sync.pool
為什么sync.pool可以減少gc壓力:
sync.pool 對象池的變量是重復使用的,高頻持續創建銷毀的場景,它用的內存一直都是對象池開辟的那一塊,所以不會導致總堆增大很多;
常見場景:緩沖緩存區
代碼示例:
var requestBodyPool = sync.Pool{New: func() interface{} {return bytes.NewBuffer(make([]byte, 0, 1024)) // 預分配 1KB 的初始容量},
}func Handler(w http.ResponseWriter, r *http.Request) {// 從池中獲取一個 Buffer,并斷言為*bytes.Bufferbuf := requestBodyPool.Get().(*bytes.Buffer)buf.Reset() // 重置數據,防止臟數據污染// 確保在處理完畢后將其放回池中defer func() {requestBodyPool.Put(buf) // 回收數據}()// ... 處理結果并返回響應
}
要注意的坑點:
1.數據取出來要斷言,斷言有panic的風險(直接封裝一個 sync.pool的結構體,能避免這個問題)
2.數據取出來后一定要 重置,不然就是臟數據
3. 最好 采用 defer xxxpool.Put(buf)的方法,不然中間panic了數據沒回收
4. 高頻持續的場景才適用 sync.pool,不然效果不大甚至反效果
5. 如果pool存的是切片,需要回收的時候可以判斷切片大小,太大的切片可以直接放棄put。因為put之后切片再對象池不會被回收,總堆會升高,這樣也會導致頻繁gc