上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
pool
并发安全对象池。通过复用,减少临时对象,降低垃圾回收压力。
池内部闲置对象(无外部引用)可被垃圾回收,无通知。
源码剖析
每个 Pool
实例管理多个 poolLocal
,与 P
对应。
动态数组 [localSize]poolLocal
,以 P.id
为索引。
// sync/pool.go type Pool struct { noCopy noCopy // local fixed-size per-P pool, actual type is [P]poolLocal local unsafe.Pointer localSize uintptr // 每次垃圾回收,都会将 local 缓存 // 改成 victim 受害缓存,以便下轮回收。 // 当 local 找不到时,也会尝试受害缓存。 victim unsafe.Pointer // local from previous cycle victimSize uintptr // size of victims array // New optionally specifies a function to generate // a value when Get would otherwise return nil. New func() any }
缓存
相关操作通过 pin
返回当前 P
所属 poolLocal
缓存。
该方法会初始化 Pool.local
内存,并添加到 allpools
全局列表。
// pin pins the current goroutine to P, disables preemption and // returns poolLocal pool for the P and the P's id. func (p *Pool) pin() (*poolLocal, int) { // P.id pid := runtime_procPin() s := runtime_LoadAcquintptr(&p.localSize) // load-acquire l := p.local // load-consume // 索引号在有效范围内。 if uintptr(pid) < s { return indexLocal(l, pid), pid } return p.pinSlow() } // poolLocal[pid] func indexLocal(l unsafe.Pointer, i int) *poolLocal { lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{})) return (*poolLocal)(lp) }
func (p *Pool) pinSlow() (*poolLocal, int) { runtime_procUnpin() // !!!! allPoolsMu.Lock() defer allPoolsMu.Unlock() pid := runtime_procPin() // 再次确认是否已存在。 s := p.localSize l := p.local if uintptr(pid) < s { return indexLocal(l, pid), pid } // 初始化 Pool.local,添加到全局列表(新建)。 // 进程内可有多个 Pool 实例,列表供 GC 清理用。 if p.local == nil { allPools = append(allPools, p) } // 分配 Pool.local 内存。 size := runtime.GOMAXPROCS(0) // Ps number local := make([]poolLocal, size) atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) runtime_StoreReluintptr(&p.localSize, uintptr(size)) return &local[pid], pid }
获取
每个 P.poolLocal
内有两处缓存。
private
: 私有。仅一个闲置对象,快速分配。shared
: 共享。可能被其他P
偷窃。
优先级:
private
>shared
>steal
>new
。
type poolLocalInternal struct { private any shared poolChain // doubly-linked list queue } type poolLocal struct { poolLocalInternal }
func (p *Pool) Get() any { // P.poolLocal。 l, pid := p.pin() // 私有对象。 x := l.private l.private = nil if x == nil { // 从公有获取。 x, _ = l.shared.popHead() // 从其他 P.poolLocal 窃取。 if x == nil { x = p.getSlow(pid) } } runtime_procUnpin() // 新建。 if x == nil && p.New != nil { x = p.New() } return x }
func (p *Pool) getSlow(pid int) any { size := runtime_LoadAcquintptr(&p.localSize) locals := p.local // 遍历所有 P.localPool.shared,偷窃。 for i := 0; i < int(size); i++ { // 这个公式让当前 pid 在最后遍历。 l := indexLocal(locals, (pid+i+1)%int(size)) if x, _ := l.shared.popTail(); x != nil { return x } } // 尝试从 victim 受害缓存中获取 ... return nil }
放回
所获取对象已从 Pool
移除,须显式放回。
func (p *Pool) Put(x any) { // 空对象,放弃。 if x == nil { return } // private。 l, _ := p.pin() if l.private == nil { l.private = x x = nil } // shared. if x != nil { l.shared.pushHead(x) } runtime_procUnpin() }
清理
当 GC 启动时,调用 clearpools
清理对象池。
// runtime/mgc.go func gcStart(trigger gcTrigger) { clearpools() } // ------------------------ var poolcleanup func() func clearpools() { // clear sync.Pools if poolcleanup != nil { poolcleanup() } }
对象池实现不属于运行时,需额外注入。
// runtime/mgc.go func sync_runtime_registerPoolCleanup(f func()) { poolcleanup = f } // -------------------------- // sync/pool.go func init() { runtime_registerPoolCleanup(poolCleanup) }
被清理的都是 Pool
内闲置对象,那些被 Get
取走的不在此列。
// sync/pool.go var ( allPools []*Pool oldPools []*Pool ) func poolCleanup() { // This function is called with the world stopped, // at the beginning of a garbage collection. // 放弃上轮受害缓存引用,使其可回收。 for _, p := range oldPools { p.victim = nil p.victimSize = 0 } // 将主缓存(local)转成受害缓存(victim)。 for _, p := range allPools { p.victim = p.local p.victimSize = p.localSize p.local = nil p.localSize = 0 } // 全局列表成老旧列表,新 allPools 由 pin 创建。 oldPools, allPools = allPools, nil }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论