Golang中存在一個Sync.Pool 對象,從名字上看像是對象池,但他本質上和實際上的對象池有著很大的區別,下麵將詳細介紹該對象。 Sync.Pool對象可伸縮、併發安全; 數據結構 sync.Pool對外暴露Get、Put、New三個方法,Get返回Pool中的對象,當沒有取得到對象時調用Ne ...
Golang中存在一個Sync.Pool 對象,從名字上看像是對象池,但他本質上和實際上的對象池有著很大的區別,下麵將詳細介紹該對象。 Sync.Pool對象可伸縮、併發安全;
數據結構
type Pool struct {
noCopy noCopy //標識不可複製對象
local unsafe.Pointer // 固定大小per-P池,實際類型[P]
PoolLocal
localSize uintptr // local大小
victim unsafe.Pointer // 來之上一個生命周期的指針,victim緩存
victimSize uintptr // victim大小
New func() interface{}
}
type poolLocalInternal struct {
private interface{} // 私有空間,只能由局部調度器P使用
shared poolChain // 共用空間,所有調度器P都可以進行相應操作,本地pushHead/popHead、任意P popTail
}
type poolLocal struct {
poolLocalInternal // poolLocal 補齊至兩個緩存行的倍數
//每個緩存行具有 64 bytes,即 512 bit
//處理器一般擁有 32 * 1024 / 64 = 512 條緩存行
//一個poolLocal與一個P綁定,也就是說一個P持有一個poolLocal。每個 poolLocal 的大小均為緩存行的偶數倍。
pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
sync.Pool對外暴露Get、Put、New三個方法,Get返回Pool中的對象,當沒有取得到對象時調用New創建新對象。Put將對象放入Pool中,這是對這三個方法的簡單理解,集合Pool的實現下麵將詳細介紹;
Sync.Pool對象獲取
對象的獲取也就是調用Pool對象上面所說的Get方法,再執行Get方法時:
1、先從私有的對象private中獲取緩存對象,如獲取失敗從共用的shared中獲取對象;
2、如還是失敗將嘗試從victim中獲取。
3、從victim中獲取對象失敗,調用New方法;
Sync.Pool對象加入
將對象加入Pool中只需要調用Put方法即可,在執行Put方法時先嘗試將對象放入私有的池private中,如private不為空這將對象放入共用的shared池中;
sync.Pool對象生命周期
Pool中對象的生命周期是不可控的,將有Go垃圾回收器進行管理,GC會清除sync.pool緩存的對象。1.13版本中引進的Victim對象可以理解為二次緩存。
在第一次執行GC時會將對象放入victim中,在此的數據還是可以獲取得到,當GC再此執行時victim中舊數據將被新淘汰得數據替換,此時數據徹底刪除;
Pool對象的緩存有效期為下下一次GC之前。
p:= sync.Pool{New: func() interface{} {
return "2"
}}
fmt.Println(p.Get()) //輸出2
p.Put("123")
p.Put("456")
runtime.GC() //清除123
fmt.Println(p.Get()) //輸出456
對象生命周期無法掌控此機制的存在,所以sync.pool對象不適合用作對象池,因為無法控制GC也就無法掌握sync.pool中對象的生命周期;
sync.Pool適合通過復用,降低複雜對象的創建和GC代價協程安全,生命周期受GC影響,不適合用於連接池等需要自己管理對象生命周期的池化。