sync.Pool会先查看自己是否有资源,有则直接返回,没有则创建一个新的,在GC的时候释放缓存的资源
使用sync.Pool是为了复用内存,减少内存分配,减轻GC压力,减少CPU资源
实现类似功能的Pool最简单的方案就是用队列,而且是并发安全的队列。
队头取,队尾放回去、在队列为空的时候创建一个新的
或者用channel也能实现一个类似的Pool
但是并发安全依赖于锁,全局锁会成为性能瓶颈
Go采取了窃取的方案来突破瓶颈
GO的设计:
每个Pool一个PoolLocal对象
每个PoolLocal有一个private 和 shared
shared指向的是一个poolChain。poolChain 的数据会被别的 Pool 给偷走
poolChain 是一个链表 + ring buffer 的双重结构
从整体上来说,它是一个双向链表
从单个节点来说,它指向了一个 ring buffer。后一个节点的 ring buffer 都是前一个节点的两倍
ring buffer 优势(实际上也可以说是数组的优势):
一次性分配好内存,循环利用
对缓存友好
GET的步骤
1 看看 private 可不可用,可用就直接返回
2 不可用则从自己的 poolChain 里面尝试获取一个
3 从头开始找。注意,头指向的其实是最近创建的 ring buffer,从队头往队尾找
4 找不到则尝试从别的 Pool 里面偷一个出来。偷的过程就是全局并发,因为理论上来说,其它 Pool 都可能恰好一起来偷了,偷是从队尾偷的
5 如果偷也偷不到,那么就会去找victim的
6 连victim的也没有,那就去创建一个新的