文章目录
什么是对象池?
对象池从字面上来理解,就是一个能存储很多对象的池子。在Go里面,对象池是通过使用sync包里的Pool结构来实现的,对象池能提高内存复用,减少内存申请次数,甚至能降低CPU消耗,是高并发项目优化不可缺少的手法之一。
作者的解释如下:
// A Pool is a set of temporary objects that may be individually saved and
// retrieved.
//
// Any item stored in the Pool may be removed automatically at any time without
// notification. If the Pool holds the only reference when this happens, the
// item might be deallocated.
//
// A Pool is safe for use by multiple goroutines simultaneously.
//
// Pool’s purpose is to cache allocated but unused items for later reuse,
// relieving pressure on the garbage collector. That is, it makes it easy to
// build efficient, thread-safe free lists. However, it is not suitable for all
// free lists.
有哪些场景需要使用对象池?
在go源码里,作者对sync.Pool使用做了如下的建议:
// An appropriate use of a Pool is to manage a group of temporary items
// silently shared among and potentially reused by concurrent independent
// clients of a package. Pool provides a way to amortize allocation overhead
// across many clients.
//
// On the other hand, a free list maintained as part of a short-lived object is
// not a suitable use for a Pool, since the overhead does not amortize well in
// that scenario. It is more efficient to have such objects implement their own
// free list.
//
// A Pool must not be copied after first use.
fmt包的应用
fmt.Sprintf()
var ppFree = sync.Pool{
New: func() interface{
} {
return new(pp) },
}
// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {
p := ppFree.Get().(*pp)
p.panicking = false
p.erroring = false
p.wrapErrs = false
p.fmt.init(&p.buf)
return p
}
// free saves used pp structs in ppFree; avoids an allocation per invocation.
func (p *pp) free() {
// Proper usage of a sync.Pool requires each entry to have approximately
// the same memory cost. To obtain this property when the stored type
// contains a variably-sized buffer, we add a hard limit on the maximum buffer
// to place back in the pool.
//
// See https://golang.org/issue/23199
if cap(p.buf) > 64<<10 {
return
}
p.buf = p.buf[:0]
p.arg = nil
p.value = reflect.Value{
}
p.wrappedErr = nil
ppFree.Put(p)
}
// Sprintf formats according to a format specifier and returns the resulting string.
func Sprintf(format string, a ...interface{
}) string {
p := newPrinter()
p.doPrintf(format, a)
s := string(p.buf)
p.free()
return s
}
字符串拼接应用
var bytePool = sync.Pool{
New: func() interface{
} {
buf := make([]byte, 0, 4096)
return buf
},
}
var ch = make(ch []byte,1000)
func main(){
go func(){
for msg := range ch {
fmt.Println("recv msg")
msg = msg[:0]
bytePool.Put(msg)
}
}
...
for i:0;i<=100000;i++ {
lineBuf := bytePool.Get().([]byte)
lineBuf = append(lineBuf, topic)
lineBuf = append(lineBuf, position)
lineBuf = append(lineBuf, info)
lineBuf = append(lineBuf, data)
ch <- lineBuf
}
time.Sleep(5 * time.Minute)
}
对象池的实现(go1.12)
相关结构定义
Pool
type Pool struct {
noCopy noCopy
local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
localSize uintptr // size of the local 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{
}
}
- noCopy 防止copy
- local 本地对象池
- localSize 本地对象池的大小
- New 生成对象的接口方法
poolLocal
type poolLocal struct {
poolLocalInternal
// Prevents false sharing on widespread platforms with
// 128 mod (cache line size) = 0 .
pad [128 - unsafe.Sizeof(poolLocalInternal{
})%128