sync.Pool 使用场景
保存和复用临时对象,减少内存分配,降低 GC 压力
例子
type Student struct { Name string Age int32 Remark [1024]byte } var buf, _ = json.Marshal(Student{Name: "Geektutu", Age: 25}) func unmarsh() { stu := &Student{} json.Unmarshal(buf, stu) }
json 反序列化在文本解析和网络通信过程中十分常见,当程序并发很高时,短时间内需要创建大量的临时变量,,这些对象分配在堆上,会给 GC 造成很大压力,严重影响程序性能。
sync.Pool 是可伸缩的,同时也是并发安全的,大小受限于内存大小。sync.Pool 用于存储那些被分配了但是没有被使用,但是未来可能被使用的值。这样可以不用再次分配内存,提高效率。
sync.Pool 是大小可伸缩的,高负载时会动态扩容,存放在池中对象不活跃会被自动清理。
如何使用
声明对象池
只要实现 New 函数即可,对象池中没有对象,那么会调用 New 函数创建
var studentPool = sync.Pool{ New: func() interface{} { return new(Student) }, }
Get& Put
stu := studentPool.Get().(*Student) json.Unmarshal(buf, stu) studentPool.Put(stu)
- Get() 用于从对象池中获取对象,因为返回值时 interface{} 因此需要值类型转换
- Put() 则是在对象使用完之后放回对象池
struct 性能测试
package sync import ( "encoding/json" "sync" "testing" ) type Student struct { Name string Age int32 Remark [1024]byte } var studentPool = sync.Pool{New: func() interface{} { return new(Student) }} var buf, _ = json.Marshal(Student{Name: "Geektutu", Age: 25}) func BenchmarkUnmarshal(b *testing.B) { for n := 0; n < b.N; n++ { stu := &Student{} json.Unmarshal(buf, stu) } } func BenchmarkUnmarshalWithPool(b *testing.B) { for n := 0; n < b.N; n++ { stu := studentPool.Get().(*Student) json.Unmarshal(buf, stu) studentPool.Put(stu) } }
执行命令
go test -bench . -benchmem
执行结果如下:
goos: darwin goarch: amd64 pkg: code.byted.org/wangmingming.hit/GoProject/main/gobase/sync cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz BenchmarkUnmarshal-12 13280 94006 ns/op 1384 B/op 7 allocs/op BenchmarkUnmarshalWithPool-12 12662 95211 ns/op 232 B/op 6 allocs/op PASS
BenchmarkUnmarshal 每个循环用了 94006 纳秒,
结果项 | 含义 |
BenchmarkUnmarshal-12 | BenchmarkUnmarshal 是测试的函数名 -12 表示GOMAXPROCS(线程数)的值为12 |
13280 | 表示一共执行了13280次,即b.N的值 |
94006.0 ns/op | 表示平均每次操作花费了94006.0纳秒 |
1384/op | 表示每次操作申请了1384 Byte的内存申请 |
7 allocs/op | 表示每次操作申请了7次内存 |
可以看到 使用 sync.Pool 后,内存占用仅为未使用的 232/1384.