中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go sync.Pool的原理及作用是什么

發布時間:2021-06-22 17:34:08 來源:億速云 閱讀:358 作者:chen 欄目:編程語言

本篇內容主要講解“Go sync.Pool的原理及作用是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Go sync.Pool的原理及作用是什么”吧!

使用方式

sync.Pool 使用很簡單,但是想用對卻很麻煩,因為你有可能看到網上一堆錯誤的示例,各位同學在搜索 sync.Pool 的使用例子時,要特別注意。

sync.Pool 是一個內存池。通常內存池是用來防止內存泄露的(例如C/C++)。sync.Pool 這個內存池卻不是干這個的,帶 GC 功能的語言都存在垃圾回收 STW 問題,需要回收的內存塊越多,STW 持續時間就越長。如果能讓 new 出來的變量,一直不被回收,得到重復利用,是不是就減輕了 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 獲取內存空間,基于這個內存做相關的處理,然后再將這個內存還回(Put)到 sync.Pool。

Pool 結構

Go sync.Pool的原理及作用是什么

sync.Pool 全景圖

源碼圖解

Go sync.Pool的原理及作用是什么

Pool.Get

Go sync.Pool的原理及作用是什么

Pool.Put

簡單點可以總結成下面的流程:

Go sync.Pool的原理及作用是什么

Pool.Get 流程

Go sync.Pool的原理及作用是什么

Pool.Put流程

Go sync.Pool的原理及作用是什么

Pool GC 流程

Sync.Pool 梳理

Pool 的內容會清理?清理會造成數據丟失嗎?

Go 會在每個 GC 周期內定期清理 sync.Pool 內的數據。

要分幾個方面來說這個問題。

  1. 已經從 sync.Pool Get 的值,在 poolClean 時雖說將 pool.local 置成了nil,Get 到的值依然是有效的,是被 GC 標記為黑色的,不會被 GC回收,當 Put 后又重新加入到 sync.Pool 中

  2. 在第一個 GC 周期內 Put 到 sync.Pool 的數值,在第二個 GC 周期沒有被 Get 使用,就會被放在 local.victim 中。如果在 第三個 GC 周期仍然沒有被使用就會被 GC 回收。

runtime.GOMAXPROCS 與 pool 之間的關系?

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) 是獲取當前最大的 p 的數量。sync.Pool 的 poolLocal 數量受 p 的數量影響,會開辟 runtime.GOMAXPROCS(0) 個 poolLocal。某些場景下我們會使用 runtime.GOMAXPROCS(N) 來改變 p 的數量,會使 sync.Pool 的 pool.poolLocal 釋放重新開辟新的空間。

為什么要開辟 runtime.GOMAXPROCS 個 local?

pool.local 是個 poolLocal 結構,這個結構體是 private + shared鏈表組成,在多 goroutine 的 Get/Put 下是有數據競爭的,如果只有一個 local 就需要加鎖來操作。每個 p 的 local 就能減少加鎖造成的數據競爭問題。

New() 的作用?假如沒有 New 會出現什么情況?

從上面的 pool.Get 流程圖可以看出來,從 sync.Pool 獲取一個內存會嘗試從當前 private,shared,其他的 p 的 shared 獲取或者 victim 獲取,如果實在獲取不到時,才會調用 New 函數來獲取。也就是 New() 函數才是真正開辟內存空間的。New() 開辟出來的的內存空間使用完畢后,調用 pool.Put 函數放入到 sync.Pool 中被重復利用。

如果 New 函數沒有被初始化會怎樣呢?很明顯,sync.Pool 就廢掉了,因為沒有了初始化內存的地方了。

先 Put,再 Get 會出現什么情況?

「一定要注意,下面這個例子的用法是錯誤的」

func main(){
    pool:= sync.Pool{
        New: func() interface{} {
            return item{}
        },
    }
    pool.Put(item{value:1})
    data := pool.Get()
    fmt.Println(data)
}

如果你直接跑這個例子,能得到你想像的結果,但是在某些情況下就不是這個結果了。

在 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 的數據結構就不支持做這個事情。

前面說使用 sync.Pool 容易被錯誤示例誤導,就是上面這個寫法。為什么 Put 的值 再 Get 會出現問題?

  • 情況1:sync.Pool 的 poolCleanup 函數在系統 GC 時會被調用,Put 到 sync.Pool 的值,由于有可能一直得不到利用,被在某個 GC 周期內就有可能被釋放掉了。

  • 情況2:不同的 goroutine 綁定的 p 有可能是不一樣的,當前 p 對應的 goroutine 放入到 sync.Pool 的值有可能被其他的 p 對應的 goroutine 取到,導致當前 goroutine 再也取不到這個值。

  • 情況3:使用 runtime.GOMAXPROCS(N) 來改變 p 的數量,會使 sync.Pool 的 pool.poolLocal 釋放重新開辟新的空間,導致 sync.Pool 被釋放掉。

  • 情況4:還有很多情況

只 Get 不 Put 會內存泄露嗎?

使用其他的池,如連接池,如果取連接使用后不放回連接池,就會出現連接池泄露,「是不是 sync.Pool 也有這個問題呢?」

通過上面的流程圖,可以看出來 Pool.Get 的時候會嘗試從當前 private,shared,其他的 p 的 shared 獲取或者 victim 獲取,如果實在獲取不到時,才會調用 New 函數來獲取,New 出來的內容本身還是受系統 GC 來控制的。所以如果我們提供的 New 實現不存在內存泄露的話,那么 sync.Pool 是不會內存泄露的。當 New 出來的變量如果不再被使用,就會被系統 GC 給回收掉。

如果不 Put 回 sync.Pool,會造成 Get 的時候每次都調用的 New 來從堆棧申請空間,達不到減輕 GC 壓力。

使用場景

上面說到 sync.Pool 業務開發中不是一個常用結構,我們業務開發中沒必要假想某塊代碼會有強烈的性能問題,一上來就用 sync.Pool 硬懟。sync.Pool 主要是為了解決 Go GC 壓力過大問題的,所以一般情況下,當線上高并發業務出現 GC 問題需要被優化時,才需要用 sync.Pool 出場。

使用注意點

  1. sync.Pool 同樣不能被復制。

  2. 好的使用習慣,從 pool.Get 出來的值進行數據的清空(reset),防止垃圾數據污染。

?  

本文基于的 Go 源碼版本:1.16.2

?

參考鏈接

  1. 深度解密 Go 語言之 sync.Pool https://www.cnblogs.com/qcrao-2018/p/12736031.html

  2. 請問sync.Pool有什么缺點? https://mp.weixin.qq.com/s/2ZC1BWTylIZMmuQ3HwrnUg

  3. Go 1.13中 sync.Pool 是如何優化的? https://colobu.com/2019/10/08/how-is-sync-Pool-improved-in-Go-1-13/

到此,相信大家對“Go sync.Pool的原理及作用是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新蔡县| 辰溪县| 承德县| 南宫市| 清涧县| 阜城县| 偃师市| 衡水市| 杨浦区| 临安市| 泰顺县| 闽清县| 乐都县| 清原| 舟山市| 穆棱市| 临沂市| 栾川县| 杭锦后旗| 广南县| 青岛市| 山丹县| 邹城市| 阿拉善右旗| 宁城县| 万源市| 南开区| 金堂县| 杭锦旗| 合山市| 榆林市| 林甸县| 福建省| 胶南市| 尉氏县| 贡嘎县| 礼泉县| SHOW| 湖口县| 福州市| 大冶市|