本篇內(nèi)容主要講解“Go sync.Pool的原理及作用是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Go sync.Pool的原理及作用是什么”吧!
為莊河等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及莊河網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站建設(shè)、成都網(wǎng)站制作、莊河網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
sync.Pool 使用很簡單,但是想用對卻很麻煩,因為你有可能看到網(wǎng)上一堆錯誤的示例,各位同學(xué)在搜索 sync.Pool 的使用例子時,要特別注意。
sync.Pool 是一個內(nèi)存池。通常內(nèi)存池是用來防止內(nèi)存泄露的(例如C/C++)。sync.Pool 這個內(nèi)存池卻不是干這個的,帶 GC 功能的語言都存在垃圾回收 STW 問題,需要回收的內(nèi)存塊越多,STW 持續(xù)時間就越長。如果能讓 new 出來的變量,一直不被回收,得到重復(fù)利用,是不是就減輕了 GC 的壓力。
正確的使用示例(下面的demo選自gin)
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) c.writermem.reset(w) c.Request = req c.reset() engine.handleHTTPRequest(c) engine.pool.Put(c)}
一定要注意的是:是先 Get 獲取內(nèi)存空間,基于這個內(nèi)存做相關(guān)的處理,然后再將這個內(nèi)存還回(Put)到 sync.Pool。
sync.Pool 全景圖
Pool.Get
Pool.Put
簡單點可以總結(jié)成下面的流程:
Pool.Get 流程
Pool.Put流程
Pool GC 流程
Go 會在每個 GC 周期內(nèi)定期清理 sync.Pool 內(nèi)的數(shù)據(jù)。
要分幾個方面來說這個問題。
已經(jīng)從 sync.Pool Get 的值,在 poolClean 時雖說將 pool.local 置成了nil,Get 到的值依然是有效的,是被 GC 標(biāo)記為黑色的,不會被 GC回收,當(dāng) Put 后又重新加入到 sync.Pool 中
在第一個 GC 周期內(nèi) Put 到 sync.Pool 的數(shù)值,在第二個 GC 周期沒有被 Get 使用,就會被放在 local.victim 中。如果在 第三個 GC 周期仍然沒有被使用就會被 GC 回收。
s := p.localSize l := p.localif uintptr(pid) < s { return indexLocal(l, pid), pid }if p.local == nil { allPools = append(allPools, p) }// If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one.size := runtime.GOMAXPROCS(0) local := make([]poolLocal, size) atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-releaseruntime_StoreReluintptr(&p.localSize, uintptr(size)) // store-release
runtime.GOMAXPROCS(0) 是獲取當(dāng)前最大的 p 的數(shù)量。sync.Pool 的 poolLocal 數(shù)量受 p 的數(shù)量影響,會開辟 runtime.GOMAXPROCS(0) 個 poolLocal。某些場景下我們會使用 runtime.GOMAXPROCS(N) 來改變 p 的數(shù)量,會使 sync.Pool 的 pool.poolLocal 釋放重新開辟新的空間。
為什么要開辟 runtime.GOMAXPROCS 個 local?
pool.local 是個 poolLocal 結(jié)構(gòu),這個結(jié)構(gòu)體是 private + shared鏈表組成,在多 goroutine 的 Get/Put 下是有數(shù)據(jù)競爭的,如果只有一個 local 就需要加鎖來操作。每個 p 的 local 就能減少加鎖造成的數(shù)據(jù)競爭問題。
從上面的 pool.Get 流程圖可以看出來,從 sync.Pool 獲取一個內(nèi)存會嘗試從當(dāng)前 private,shared,其他的 p 的 shared 獲取或者 victim 獲取,如果實在獲取不到時,才會調(diào)用 New 函數(shù)來獲取。也就是 New() 函數(shù)才是真正開辟內(nèi)存空間的。New() 開辟出來的的內(nèi)存空間使用完畢后,調(diào)用 pool.Put 函數(shù)放入到 sync.Pool 中被重復(fù)利用。
如果 New 函數(shù)沒有被初始化會怎樣呢?很明顯,sync.Pool 就廢掉了,因為沒有了初始化內(nèi)存的地方了。
「一定要注意,下面這個例子的用法是錯誤的」
func main(){ pool:= sync.Pool{ New: func() interface{} { return item{} }, } pool.Put(item{value:1}) data := pool.Get() fmt.Println(data) }
如果你直接跑這個例子,能得到你想像的結(jié)果,但是在某些情況下就不是這個結(jié)果了。
在 Pool.Get 注釋里面有這么一句話:“Callers should not assume any relation between values passed to Put and the values returned by Get.”,告訴我們不能把值 Pool.Put 到 sync.Pool 中,再使用 Pool.Get 取出來,因為 sync.Pool 不是 map 或者 slice,放入的值是有可能拿不到的,sync.Pool 的數(shù)據(jù)結(jié)構(gòu)就不支持做這個事情。
前面說使用 sync.Pool 容易被錯誤示例誤導(dǎo),就是上面這個寫法。為什么 Put 的值 再 Get 會出現(xiàn)問題?
情況1:sync.Pool 的 poolCleanup 函數(shù)在系統(tǒng) GC 時會被調(diào)用,Put 到 sync.Pool 的值,由于有可能一直得不到利用,被在某個 GC 周期內(nèi)就有可能被釋放掉了。
情況2:不同的 goroutine 綁定的 p 有可能是不一樣的,當(dāng)前 p 對應(yīng)的 goroutine 放入到 sync.Pool 的值有可能被其他的 p 對應(yīng)的 goroutine 取到,導(dǎo)致當(dāng)前 goroutine 再也取不到這個值。
情況3:使用 runtime.GOMAXPROCS(N) 來改變 p 的數(shù)量,會使 sync.Pool 的 pool.poolLocal 釋放重新開辟新的空間,導(dǎo)致 sync.Pool 被釋放掉。
情況4:還有很多情況
使用其他的池,如連接池,如果取連接使用后不放回連接池,就會出現(xiàn)連接池泄露,「是不是 sync.Pool 也有這個問題呢?」
通過上面的流程圖,可以看出來 Pool.Get 的時候會嘗試從當(dāng)前 private,shared,其他的 p 的 shared 獲取或者 victim 獲取,如果實在獲取不到時,才會調(diào)用 New 函數(shù)來獲取,New 出來的內(nèi)容本身還是受系統(tǒng) GC 來控制的。所以如果我們提供的 New 實現(xiàn)不存在內(nèi)存泄露的話,那么 sync.Pool 是不會內(nèi)存泄露的。當(dāng) New 出來的變量如果不再被使用,就會被系統(tǒng) GC 給回收掉。
如果不 Put 回 sync.Pool,會造成 Get 的時候每次都調(diào)用的 New 來從堆棧申請空間,達(dá)不到減輕 GC 壓力。
上面說到 sync.Pool 業(yè)務(wù)開發(fā)中不是一個常用結(jié)構(gòu),我們業(yè)務(wù)開發(fā)中沒必要假想某塊代碼會有強(qiáng)烈的性能問題,一上來就用 sync.Pool 硬懟。sync.Pool 主要是為了解決 Go GC 壓力過大問題的,所以一般情況下,當(dāng)線上高并發(fā)業(yè)務(wù)出現(xiàn) GC 問題需要被優(yōu)化時,才需要用 sync.Pool 出場。
sync.Pool 同樣不能被復(fù)制。
好的使用習(xí)慣,從 pool.Get 出來的值進(jìn)行數(shù)據(jù)的清空(reset),防止垃圾數(shù)據(jù)污染。
?本文基于的 Go 源碼版本:1.16.2
?
深度解密 Go 語言之 sync.Pool https://www.cnblogs.com/qcrao-2018/p/12736031.html
請問sync.Pool有什么缺點? https://mp.weixin.qq.com/s/2ZC1BWTylIZMmuQ3HwrnUg
Go 1.13中 sync.Pool 是如何優(yōu)化的? https://colobu.com/2019/10/08/how-is-sync-Pool-improved-in-Go-1-13/
到此,相信大家對“Go sync.Pool的原理及作用是什么”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
新聞標(biāo)題:Gosync.Pool的原理及作用是什么
轉(zhuǎn)載注明:http://aaarwkj.com/article38/jjhjpp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、App設(shè)計、云服務(wù)器、服務(wù)器托管、電子商務(wù)、動態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)