有使用过 Pool 吗?
有使用过 Pool 吗?
提示
题眼: 池化技术。
前置知识点:池化技术
池化技术是一种常见的软件设计模式,核心思想是预先创建并管理一组可复用资源。需要使用时从池中获取,用完后归还到池中,而不是直接销毁。它的主要目标是复用资源、降低创建销毁成本、减少系统开销。
Go 语言里的 sync.Pool 和线程池、连接池不完全一样,它主要用于缓存临时对象,减少频繁分配内存和垃圾回收带来的压力。但它不适合保存长期对象,因为池里的内容在 GC 时可能被清理。
sync.Pool 最常用的三个成员是:
New:当池里没有可用对象时,负责创建新对象。Get:优先从本地缓存取对象,没有再尝试从共享区域取,最后再走New。Put:把对象放回池中,供后续复用。
它的底层大体延续了“本地缓存 + 共享缓存”的思路,尽量减少全局加锁竞争。
type Pool struct {
noCopy noCopy
local unsafe.Pointer // 本地缓存,指向一个 [P]poolLocal 数组
localSize uintptr // 本地缓存的大小
New func() interface{} // 创建新对象的函数
}
type poolLocal struct {
poolLocalInternal
// 避免伪共享
pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
type poolLocalInternal struct {
private interface{} // 私有对象,只能被当前 P 使用
shared poolChain // 共享对象链表
}相关信息
参考: Gin 的 Context 实现。
重要
参考答案:
sync.Pool 本质上是一个临时对象池,适合装那些“创建成本不算低、但生命周期很短”的对象,比如缓冲区、临时结构体、编解码中间对象等。
它最大的特点是复用内存,减少 GC 压力。调用 Get 时优先从本地缓存拿,拿不到再去共享区域找,还没有就调用 New 创建;调用 Put 时再把对象放回池里。这种设计可以降低高并发下的锁竞争。
但它也有很明显的边界。第一,sync.Pool 里的对象不是永久保存的,GC 发生时可能会被整体清掉,所以不能把它当缓存使用。第二,它更适合短期临时对象,不适合拿来管理数据库连接、线程之类需要长期持有的资源。
在实际项目里,Web 框架和高性能库里比较常见,比如 Gin 就会用它来复用 Context 对象。
相关信息
引申: 什么是线程池?什么是连接池?
