sync.Pool适合高频创建、易重置、线程安全复用的无状态对象,如[]byte、bytes.Buffer、jsON解析器;不适合持有外部资源或状态难清理的对象,使用时须手动Reset并及时Put。

go 语言中频繁创建和销毁小对象(比如 Struct、[]byte、bytes.Buffer)会增加 GC 压力,拖慢性能。用 sync.Pool 复用对象能显著减少堆分配和 GC 次数,尤其适合生命周期短、可重用、无状态或易重置的对象。
哪些对象适合放进 sync.Pool?
不是所有对象都适合池化。关键看三点:是否高频创建、是否容易重置、是否线程安全复用。
- 适合:临时切片(
[]byte)、缓冲区(bytes.Buffer)、解析器结构体(如 json 解析上下文)、http 中间件用的上下文容器 - 不适合:持有外部资源(文件句柄、DB 连接)、带未清理状态且重置成本高、有跨 goroutine 生命周期依赖的对象
- 注意:
sync.Pool不保证对象一定被复用,GC 时可能被全部清理;也不能替代连接池(如sql.DB)
正确声明和使用 Pool 实例
Pool 应定义为包级变量,避免每次调用都新建;New 字段必须返回**新初始化的对象**,不能返回 nil 或共享实例。
var bufPool = sync.Pool{ New: func() Interface{} { return new(bytes.Buffer) // 每次 New 返回全新 Buffer }, }
使用时先 Get(),用完立刻 Put(),别依赖 defer(可能延迟太晚,影响复用效率):
立即学习“go语言免费学习笔记(深入)”;
buf := bufPool.Get().(*bytes.Buffer) buf.Reset() // 必须手动清空状态!Buffer 不自动清空 buf.WriteString("hello") // ... 使用 buf bufPool.Put(buf) // 立即归还
如果忘记 Reset(),下次 Get 到的可能是脏数据 —— 这是常见 bug 来源。
避免常见陷阱
- 类型断言失败:Pool 存的是
interface{},Get 后务必做类型检查或断言,建议用 ok-idiom 防 panic - 误存指针或闭包引用:若 Put 的对象间接引用了大内存(如闭包捕获了大 slice),会导致整块内存无法回收
- 滥用在长生命周期场景:Pool 对象可能被 GC 清理,不能用于需要稳定存在几秒以上的对象
- 不测不优化:加 Pool 前后用
go test -bench和-memprofile对比分配次数和 GC 时间,确认真有收益
基本上就这些。sync.Pool 不复杂但容易忽略 Reset 和类型安全,用对了就是零成本的性能提升。