上卷 程序设计
中卷 标准库
- 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.5 清理
默认情况下,采取后台并发模式执行清理操作。
// mgc.go func gcSweep(mode gcMode) { // 累加代龄,重置结束标志。 mheap_.sweepgen += 2 mheap_.sweepdone = 0 // 阻塞模式。 if !_ConcurrentSweep || mode == gcForceBlockMode { // 循环清理所有 span。 for sweepone() != ^uintptr(0) { sweep.npausesweep++ // 阻塞清理计数。 } return } // 并发模式。 if sweep.parked { sweep.parked = false ready(sweep.g, 0, true) } }
bgsweep
并发模式通过 bgsweep goroutine 执行。
它在 runtime.main 创建后处于待命状态。
// proc.go // The main goroutine. func main() { gcenable() }
// mgc.go func gcenable() { go bgsweep(c) <-c memstats.enablegc = true // now that runtime is initialized, GC is okay }
// mgcsweep.go func bgsweep(c chan int) { sweep.parked = true c <- 1 // 休眠。等待 gcSweep 唤醒。 goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) for { // 循环从待清理栈提取 span,执行清理操作。 for sweepone() != ^uintptr(0) { sweep.nbgsweep++ // 并发清理计数。 Gosched() } // 休眠,等待下次唤醒。 goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) } }
sweepgen
在 heap 里,专门存储清理代。
// mheap.go type mheap struct { sweepgen uint32 // sweep generation, see comment in mspan sweepdone uint32 // all spans are swept }
// sweep generation: // if sweepgen == h->sweepgen - 2, the span needs sweeping // if sweepgen == h->sweepgen - 1, the span is currently being swept // if sweepgen == h->sweepgen, the span is swept and ready to use // if sweepgen == h->sweepgen + 1, the span was cached before sweep began and is still cached, and needs sweeping // if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached // h->sweepgen is incremented by 2 after every GC
从 heap 获取内存的 allocSpan 方法里,会设置当前代龄。
// mheap.go func (h *mheap) allocSpan(npages uintptr, manual bool, spanclass spanClass, sysStat *uint64) (s *mspan) { ... HaveSpan: atomic.Store(&s.sweepgen, h.sweepgen) s.state.set(mSpanInUse) }
sweepone
从待清理栈取出一个 span,执行清理操作。
当 mcache 需要扩充内存时,先调用 mcentral.cacheSpan,其次才是 central.grow -> mheap.allocSpan。
用 allocSpan 从堆获取内存时,并不会将 span 添加到 mcentral.full/partial 管理链表。
直到垃圾回收,才调用 mcentral.uncacheSpan 和 mspan.sweep 将其添加到管理链表。
另外,mcache 调用 mcentral.cacheSpan 扩容时,尝试主动清理。
加上 markroot,调用 uncacheSpan 收回 mcahce 所持有 span,所以再次获取必然是已经清理过的。
// mgcsweep.go // sweeps one span // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep func sweepone() uintptr { var s *mspan sg := mheap_.sweepgen // 查找一个待清理 span。 for { // 从待清理栈里提取 span,执行清理操作。 s = mheap_.nextSpanForSweep() if s == nil { atomic.Store(&mheap_.sweepdone, 1) // 结束标志。 break } // 跳过闲置的 span。 if state := s.state.get(); s.state != mSpanInUse { continue } // 找到目标,调整代龄。 if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { break } } // 清理找到的 span。 npages := ^uintptr(0) if s != nil { npages = s.npages // 参考《内存分配 - 回收》。 s.sweep(false) } return npages }
// mgcsweep.go // nextSpanForSweep finds and pops the next span for sweeping from the // central sweep buffers. It returns ownership of the span to the caller. // Returns nil if no such span exists. func (h *mheap) nextSpanForSweep() *mspan { sg := h.sweepgen // 循环检查所有 central。 for sc := sweep.centralIndex.load(); sc < numSweepClasses; sc++ { spc, full := sc.split() c := &h.central[spc].mcentral // 分别检查 full 和 partail 未清理列表。 var s *mspan if full { s = c.fullUnswept(sg).pop() } else { s = c.partialUnswept(sg).pop() } if s != nil { // Write down that we found something so future sweepers // can start from here. sweep.centralIndex.update(sc) return s } } // Write down that we found nothing. sweep.centralIndex.update(sweepClassDone) return nil }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论