sync.Pool
Pool
1、用于存储拿些被分配了但是没有被使用,而未来可能会被使用的值,以减小垃圾回收的压力 2、协程安全,应该用于管理协程共享的变量,不推荐用于非协程间的对象管理 3、调动New函数,将使用函数创建一个新对象返回 4、从Pool中取出对象时,如果Pool中没有对象,将执行New(),如果没有对New进行赋值,则返回nil 5、先进后出存储原则,和栈类似Pool 一个比较好的例子是fmt包,fmt包总是需要使用一些[]byte之类的对象, Go建立了一个临时对象池存放看这些对象,如果需要使用一个[]byte,就去Pool里拿,如果拿不到就分配一份。 这比起不停 生成新的[]byte,用完了在等待gc回收要高效的多
应用场景 1、路由映射表
type Pool struct { noCopy noCopy // 一个空结构,用于放置 pool在第一次使用后被复制 local unsafe.Pointer // per-P pool, 实际类型 [P]poolLocal localSize uintptr // local的代销 victim unsafe.Pointer // local from previous cycle victimSize uintptr // size of victims array // New optionally specifies a function to generate // a value when Get would otherwise return nil. // It may not be changed concurrently with calls to Get. New func() interface{} //在pool中没有获取到对象的话,调用该方法生成一个变量,默认返回nil } // 具有存储结构 type poolLocalInternal struct { private interface{} // 只能有自己的P使用 shared poolChain // 可以被任何P使用 } type poolLocal struct { poolLocalInternal // Prevents false sharing on widespread platforms with // 128 mod (cache line size) = 0 .避免缓存false,使不同的线程操作那个不同的缓存行,多喝的情况下提升效率 pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte }
var (
allPoolsMu Mutex 保护shared线程安全
// allPools is the set of pools that have non-empty primary
// caches. Protected by either 1) allPoolsMu and pinning or 2)
// STW.
allPools []*Pool 池列表
// oldPools is the set of pools that may have non-empty victim
// caches. Protected by STW.
oldPools []*Pool
)
func (p *Pool) Put(x interface{}) { 。。。 l, _ := p.pin() // 获取当前P的pool if l.private == nil { //私有属性为空,放入 l.private = x x = nil } if x != nil { l.shared.pushHead(x) // 放入shared池 } 。。。 }
func (p *Pool) Get() interface{} { 。。。 l, pid := p.pin() // 获取当前P的pool x := l.private // 先从private读取 l.private = nil if x == nil { // private没有 x, _ = l.shared.popHead() // 从当前P的shared末尾取一个 if x == nil { // 还没有取到,则去其他的P的shared取 x = p.getSlow(pid)
func (p *Pool) getSlow(pid int) interface{} { //... // 尝试从其他的P中获取一个元素 for i := 0; i < int(size); i++ { // 获取其他P的的poolLocal l := indexLocal(locals, (pid+i+1)%int(size)) if x, _ := l.shared.popTail(); x != nil { return x } } //... }
}
} runtime_procUnpin() if race.Enabled { race.Enable() if x != nil { race.Acquire(poolRaceAddr(x)) } }// 最后还没有取到,调用NEW方法生成一个 if x == nil && p.New != nil { x = p.New() } return x }

GC
func init() { runtime_registerPoolCleanup(poolCleanup)//GC之前运行该函数 } func poolCleanup() { // 将所有池的变量解除引用,未下一次GC做准备
1、在GC时会调用此函数
他不能分配,也不应该调用任何运行时函数
防御性地将所有东西归零,原因:
1、防止整个个池的错误保留
2、如果GC发生时goroutine与PUT.GET中的 l.shared一起使用,他将保留zhenggePool.因此下一周去内存消耗将增加一倍
for _, p := range oldPools {
// 清除将所有池对象接触引用 等待GC回收 p.victim = nil p.victimSize = 0 } // Move primary cache to victim cache. for _, p := range allPools { p.victim = p.local p.victimSize = p.localSize p.local = nil p.localSize = 0 } // The pools with non-empty primary caches now have non-empty // victim caches and no pools have primary caches. oldPools, allPools = allPools, nil }
echo
在 echo 中主要用来存储 context,因为大量的 foroutines 不断申请 context 的内存,
会给 GC 带来大的压力影响性能。所以 echo 采用 sync.pool 来优化。
// New creates an instance of Echo. func New() (e *Echo) { ... e.pool.New = func() interface{} { return e.NewContext(nil, nil) } e.router = NewRouter(e) return } // NewContext returns a Context instance. func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context { return &context{ request: r, response: NewResponse(w, e), store: make(Map), echo: e, pvalues: make([]string, *e.maxParam), handler: NotFoundHandler, } } // AcquireContext returns an empty `Context` instance from the pool. // You must return the context by calling `ReleaseContext()`. func (e *Echo) AcquireContext() Context { return e.pool.Get().(Context) } // ReleaseContext returns the `Context` instance back to the pool. // You must call it after `AcquireContext()`. func (e *Echo) ReleaseContext(c Context) { e.pool.Put(c) }
看完定义,我们再看看,echo 里的使用。也就是说我们通过 pool 这种形式避免了在并发大的情况下,造成的内存申请,和 GC 的压力。
// http 请求处理方法 func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 从池里获取一个 context 对象 c := e.pool.Get().(*context) // 重置对象 c.Reset(r, w) ... // 用完后把 context e.pool.Put(c) }
性能测试
package main import ( "sync" "testing" ) const size = 10240 const count = 102400 // 开辟的内存小的话,不用使用Pool func BenchmarkWithPool(b *testing.B) { var pool = sync.Pool{ New: func() interface{} { a := make([]int, size) return &a }, } for i := 0; i < b.N; i++ { for j := 0; j < count; j++ { s := pool.Get().(*[]int) pool.Put(s) } } } func BenchmarkWithNoPool(b *testing.B) { for i := 0; i < b.N; i++ { for j := 0; j < count; j++ { _ = make([]int, size) } } } // -benchtime=20s -cpu=1go test -bench=. -benchmem

浙公网安备 33010602011771号