示例说明
高 QPS 路径上每次请求都新建缓冲区会把 GC 压力推高——分配、扫描、回收全程开销。sync.Pool 的对象池让缓冲区在请求间复用:Get 取出任意空闲对象,用完 Put 归还,池空时 New 构造新对象兜底。归还前 Reset 是正确性红线——不清零会把上一个使用者的数据泄漏给下一个。
技术要点
- Get 与 Put 严格配对成生命周期
- 归还前 Reset 防跨请求数据串味
- New 兜底构造保证池空不失败
代码解读
renderJSON 从池里取缓冲,defer 里先 Reset 再归还——defer 的执行顺序保证异常路径也会归还;写入用 WriteString 与 Fprintf 组合避免拼接临时串。main 的并发循环演示多 goroutine 共享池的安全性:池本身并发安全,业务侧只需归还纪律。
使用建议
池对象不宜过大,巨型缓冲会撑住堆不释放。GC 清空池是正常行为,别指望池永久保量。存接口值有装箱开销,指针类型最合适。
代码示例
package main
import (
"bytes"
"fmt"
"sync"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer) // 池空时的兜底构造
},
}
func renderJSON(name string, score int) string {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset() // 必须清零,防止跨使用者数据泄漏
bufferPool.Put(buf) // 归还前重置容量状态
}()
buf.WriteString("{\"name\":\"")
buf.WriteString(name)
buf.WriteString("\",\"score\":")
fmt.Fprintf(buf, "%d}", score)
return buf.String()
}
func main() {
results := make([]string, 0, 8)
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
results = append(results[:n:n], renderJSON(fmt.Sprintf("user-%d", n), n*10))
}(i)
}
wg.Wait()
for _, r := range results {
fmt.Println(r)
}
}