其他
- gcWork
每个 P 上都有一个 gcw 用来管理灰色对象(get 和 put),gcw 的结构就是 gcWork。gcWork 中的核心是 wbuf1 和 wbuf2,里面存储就是灰色对象,或者说是 work(下面就全部统一叫做 work)。
type p struct {...gcw gcWork}type gcWork struct {// wbuf1 and wbuf2 are the primary and secondary work buffers.wbuf1, wbuf2 wbufptr// Bytes marked (blackened) on this gcWork. This is aggregated// into work.bytesMarked by dispose.bytesMarked uint64// Scan work performed on this gcWork. This is aggregated into// gcController by dispose and may also be flushed by callers.scanWork int64}
既然每个 P 上有一个 work buffer,那么是不是还有一个全局的 work list 呢?是的。通过在每个 P 上绑定一个 work buffer 的好处和 cache 一样,不需要加锁。
var work struct {full uint64 // lock-free list of full blocks workbufempty uint64 // lock-free list of empty blocks workbufpad0 [sys.CacheLineSize]uint8 // prevents false-sharing between full/empty and nproc/nwait...}
那么为什么使用两个 work buffer (wbuf1 和 wbuf2)呢?例如我现在要 get 一个 work 出来,先从 wbuf1 中取,wbuf1 为空的话则与 wbuf2 swap 再 get。在其他时间将 work buffer 中的 full 或者 empty buffer 移到 global 的 work 中。 这样的好处在于,在 get 的时候去全局的 work 里面取(多个 goroutine 去取会有竞争)。这里有趣的是 global 的 work list 是 lock-free 的,通过原子操作 cas 等实现。下面列举几个函数看一下 gcWrok。
初始化
func (w *gcWork) init() {w.wbuf1 = wbufptrOf(getempty())wbuf2 := trygetfull()if wbuf2 == nil {wbuf2 = getempty()}w.wbuf2 = wbufptrOf(wbuf2)}
put
// put enqueues a pointer for the garbage collector to trace.// obj must point to the beginning of a heap object or an oblet.func (w *gcWork) put(obj uintptr) {wbuf := w.wbuf1.ptr()if wbuf == nil {w.init()wbuf = w.wbuf1.ptr()// wbuf is empty at this point.} else if wbuf.nobj == len(wbuf.obj) {w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1wbuf = w.wbuf1.ptr()if wbuf.nobj == len(wbuf.obj) {putfull(wbuf)wbuf = getempty()w.wbuf1 = wbufptrOf(wbuf)flushed = true}}wbuf.obj[wbuf.nobj] = objwbuf.nobj++}
- get
// get dequeues a pointer for the garbage collector to trace, blocking// if necessary to ensure all pointers from all queues and caches have// been retrieved. get returns 0 if there are no pointers remaining.//go:nowritebarrierfunc (w *gcWork) get() uintptr {wbuf := w.wbuf1.ptr()if wbuf == nil {w.init()wbuf = w.wbuf1.ptr()// wbuf is empty at this point.}if wbuf.nobj == 0 {w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1wbuf = w.wbuf1.ptr()if wbuf.nobj == 0 {owbuf := wbufwbuf = getfull()if wbuf == nil {return 0}putempty(owbuf)w.wbuf1 = wbufptrOf(wbuf)}}// TODO: This might be a good place to add prefetch codewbuf.nobj--return wbuf.obj[wbuf.nobj]}