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