上卷 程序设计
中卷 标准库
- 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
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
3.6.1 写屏障
已扫描黑色对象,可能被并发用户代码修改。此时,写屏障会拦截指针修改操作,使其被重新处理。
// test/main.go package main var x *int func main() { x = new(int) println(x) }
$ go build -gcflags "-S" # test "".main STEXT size=118 args=0x0 locals=0x18 funcid=0x0 align=0x0 0x0000 00000 TEXT "".main(SB), ABIInternal, $24-0 0x0006 00006 SUBQ $24, SP 0x000a 00010 MOVQ BP, 16(SP) 0x000f 00015 LEAQ 16(SP), BP 0x0014 00020 LEAQ type.int(SB), AX 0x001b 00027 NOP 0x0020 00032 CALL runtime.newobject(SB) // 判断写屏障是否启用。(writeBarrier.enabled != 0) 0x0025 00037 CMPL runtime.writeBarrier(SB), $0 0x002c 00044 JNE 55 0x002e 00046 MOVQ AX, "".x(SB) 0x0035 00053 JMP 69 // 写屏障拦截。 0x0037 00055 LEAQ "".x(SB), DI 0x003e 00062 NOP 0x0040 00064 CALL runtime.gcWriteBarrier(SB) 0x0045 00069 CALL runtime.printlock(SB) 0x004a 00074 MOVQ "".x(SB), AX 0x0051 00081 CALL runtime.printpointer(SB) 0x0056 00086 CALL runtime.printnl(SB) 0x0060 00096 CALL runtime.printunlock(SB) 0x0065 00101 MOVQ 16(SP), BP 0x006a 00106 ADDQ $24, SP 0x006e 00110 RET
从输出汇编可以看出,编译器插入写屏障相关代码。
// mgc.go var writeBarrier struct { enabled bool // compiler emits a check of this before calling write barrier } func setGCPhase(x uint32) { atomic.Store(&gcphase, x) writeBarrier.needed = gcphase == _GCmark || gcphase == _GCmarktermination writeBarrier.enabled = writeBarrier.needed || writeBarrier.cgo }
拦截代码以汇编实现,将新老两个目标对象放入 P.wbBuf 内。
// runtime2.go type p struct { // wbBuf is this P's GC write barrier buffer. wbBuf wbBuf }
// mwbbuf.go // wbBuf is a per-P buffer of pointers queued by the write barrier. // This buffer is flushed to the GC workbufs when it fills up and on // various GC transitions. // +----------------------+ next += ptrsize * 2 // | | // | v // +------+-----+-------------+-------------+-----+ // | next | end | old0 | new0 | old1 | new1 | ... | buf[256*2] // +------+-----+-------------+-------------+-----+ // | ^ // | | // +------------------------------------+ type wbBuf struct { // next points to the next slot in buf. next uintptr // end points to just past the end of buf. end uintptr // buf stores a series of pointers to execute write barriers // on. This must be a multiple of wbBufEntryPointers because // the write barrier only checks for overflow once per entry. buf [wbBufEntryPointers * wbBufEntries]uintptr }
// asm_amd64.s // gcWriteBarrier performs a heap pointer write and informs the GC. // // gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: // - DI is the destination of the write // - AX is the value being written at DI TEXT runtime·gcWriteBarrier<ABIInternal>(SB),NOSPLIT,$112 // across a sequence of write barriers. MOVQ g_m(R14), R13 // m MOVQ m_p(R13), R13 // p MOVQ (p_wbBuf+wbBuf_next)(R13), R12 // p.wbBuf.next // Increment wbBuf.next position. LEAQ 16(R12), R12 MOVQ R12, (p_wbBuf+wbBuf_next)(R13) // next 指向下一组,预留一组位置。 CMPQ R12, (p_wbBuf+wbBuf_end)(R13) // next 与 end 比较,是否满了? // Record the write. MOVQ AX, -16(R12) // Record value // 在预留位置保存 AX 和 (DI)。 MOVQ (DI), R13 MOVQ R13, -8(R12) // Record *slot // Is the buffer full? (flags set in CMPQ above) // 满了,就主动提交。 JEQ flush ret: // Do the write. MOVQ AX, (DI) // 已拦截,还是要完成用户写入操作。 RET flush: CALL runtime·wbBufFlush(SB) // 主动提交到队列。 JMP ret
当工人调用 gcDrain 时,会检查并处理写屏障缓存。
// mgcmark.go func gcDrain(gcw *gcWork, flags gcDrainFlags) { // Drain root marking jobs ... // Drain heap marking jobs. for !(gp.preempt && (preemptible || atomic.Load(&sched.gcwaiting) != 0)) { b := gcw.tryGetFast() if b == 0 { b = gcw.tryGet() // 灰色队列没找到,处理写屏障缓存。 if b == 0 { // Flush the write barrier // buffer; this may create // more work. wbBufFlush(nil, 0) b = gcw.tryGet() } } if b == 0 { // Unable to get work. break } scanobject(b, gcw) } }
// wbBufFlush flushes the current P's write barrier buffer to the GC // workbufs. It is passed the slot and value of the write barrier that // caused the flush so that it can implement cgocheck. func wbBufFlush(dst *uintptr, src uintptr) { wbBufFlush1(getg().m.p.ptr()) }
// wbBufFlush1 flushes p's write barrier buffer to the GC work queue. func wbBufFlush1(_p_ *p) { // 将 buf 数组转换为指针切片。 start := uintptr(unsafe.Pointer(&_p_.wbBuf.buf[0])) n := (_p_.wbBuf.next - start) / unsafe.Sizeof(_p_.wbBuf.buf[0]) ptrs := _p_.wbBuf.buf[:n] _p_.wbBuf.next = 0 gcw := &_p_.gcw pos := 0 // 扫描新、旧对象。 for _, ptr := range ptrs { obj, span, objIndex := findObject(ptr, 0, 0) if obj == 0 { continue } // 检查标记。 mbits := span.markBitsForIndex(objIndex) if mbits.isMarked() { continue } // 新标记。 mbits.setMarked() // 没有指针,无需加入灰色队列。 if span.spanclass.noscan() { continue } // 新标记对象暂存在头部已检查区域。 ptrs[pos] = obj pos++ } // 将新标记对象放入灰色队列。 gcw.putBatch(ptrs[:pos]) // 重置写屏障缓冲。 _p_.wbBuf.reset() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论