上卷 程序设计
中卷 标准库
- 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.3.1 触发
除因为内存分配触发阈值引起回收外,还有系统监控等引发的强制回收。
// mgc.go const ( // gcTriggerHeap indicates that a cycle should be started when // the heap size reaches the trigger heap size computed by the // controller. gcTriggerHeap gcTriggerKind = iota // gcTriggerTime indicates that a cycle should be started when // it's been more than forcegcperiod nanoseconds since the // previous GC cycle. gcTriggerTime // gcTriggerCycle indicates that a cycle should be started if // we have not yet started cycle number gcTrigger.n (relative // to work.cycles). gcTriggerCycle )
分配内存时,并非每次都检查。仅大块分配才会进行(shouldhelpgc),如扩容、大对象等。
// malloc.go func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { shouldhelpgc := false if size <= maxSmallSize { v, span, shouldhelpgc = c.nextFree(tinySpanClass) } else { shouldhelpgc = true span = c.allocLarge(size, noscan) } }
// malloc.go func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bool) { shouldhelpgc = false if freeIndex == s.nelems { // 扩容。 c.refill(spc) shouldhelpgc = true } }
测试是否达到触发条件。
// malloc.go func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { if shouldhelpgc { if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { gcStart(t) } } }
// mgc.go // A gcTrigger is a predicate for starting a GC cycle. Specifically, // it is an exit condition for the _GCoff phase. type gcTrigger struct { kind gcTriggerKind now int64 // gcTriggerTime: current time n uint32 // gcTriggerCycle: cycle number to start } type gcTriggerKind int
// mgc.go // test reports whether the trigger condition is satisfied, meaning // that the exit condition for the _GCoff phase has been met. The exit // condition should be tested when allocating. func (t gcTrigger) test() bool { switch t.kind { case gcTriggerHeap: return gcController.heapLive >= gcController.trigger case gcTriggerTime : ... case gcTriggerCycle: ... } return true }
强制回收
强制启动垃圾回收分手动和自动两种。手动是用户直接调用 runtime.GC,自动则由 sysmon 启动。
runtime.GC
当我们调用 runtime.GC 时,可能后台正在并行执行第 N 次垃圾回收。不管 N 处于回收周期的哪个阶段,都应该先等它结束。直到 N 彻底完成后,才开始我们的 N + 1 回收周期,直到清理(sweep)结束。注意,该函数并未调用 test 检查触发条件,且会阻塞用户代码。
// mgc.go // GC runs a garbage collection and blocks the caller until the // garbage collection is complete. It may also block the entire // program. func GC() { // We consider a cycle to be: sweep termination, mark, mark // termination, and sweep. This function shouldn't return // until a full cycle has been completed, from beginning to // end. Hence, we always want to finish up the current cycle // and start a new one. That means: // // 1. In sweep termination, mark, or mark termination of cycle // N, wait until mark termination N completes and transitions // to sweep N. // // 2. In sweep N, help with sweep N. // // At this point we can begin a full cycle N+1. // // 3. Trigger cycle N+1 by starting sweep termination N+1. // // 4. Wait for mark termination N+1 to complete. // // 5. Help with sweep N+1 until it's done. // // This all has to be written to deal with the fact that the // GC may move ahead on its own. For example, when we block // until mark termination N, we may wake up in cycle N+2. // 等待 N 标记结束。 n := atomic.Load(&work.cycles) gcWaitOnMark(n) // 启动 N + 1 周期。 gcStart(gcTrigger{kind: gcTriggerCycle, n: n + 1}) // 等待 N + 1 标记结束 gcWaitOnMark(n + 1) // 执行清理任务。 for atomic.Load(&work.cycles) == n+1 && sweepone() != ^uintptr(0) { sweep.nbgsweep++ // 非后台清理。 Gosched() } // 等待清理结束。 for atomic.Load(&work.cycles) == n+1 && !isSweepDone() { Gosched() } }
// mgc.go // gcWaitOnMark blocks until GC finishes the Nth mark phase. If GC has // already completed this mark phase, it returns immediately. func gcWaitOnMark(n uint32) { for { // Disable phase transitions. lock(&work.sweepWaiters.lock) // 当前周期序号。 nMarks := atomic.Load(&work.cycles) // 已完成标记。 if gcphase != _GCmark { // We've already completed this cycle's mark. nMarks++ } // 当前周期超过 n,结束。 if nMarks > n { // We're done. unlock(&work.sweepWaiters.lock) return } // gcStart: work.cycles++ // gcMarkTermination: wake // Wait until sweep termination, mark, and mark // termination of cycle N complete. work.sweepWaiters.list.push(getg()) goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceEvGoBlock, 1) } }
sysmon
如垃圾回收已长时间未执行,那么也会强制启动。
// proc.go func sysmon() { for { usleep(delay) ... // check if we need to force a GC if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { lock(&forcegc.lock) forcegc.idle = 0 // 唤醒。(将 forcegc G 放回任务队列) var list gList list.push(forcegc.g) injectglist(&list) unlock(&forcegc.lock) } } }
// mgc.go func (t gcTrigger) test() bool { switch t.kind { case gcTriggerHeap: ... case gcTriggerTime: // GOGC < 0, OFF! if gcController.gcPercent.Load() < 0 { return false } // 超过 2 分钟。 lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime)) return lastgc != 0 && t.now-lastgc > forcegcperiod case gcTriggerCycle: ... } return true }
// proc.go // forcegcperiod is the maximum time in nanoseconds between garbage // collections. If we go this long without a garbage collection, one // is forced to run. // // This is a variable for testing purposes. It normally doesn't change. var forcegcperiod int64 = 2 * 60 * 1e9
专用 forcegc G 启动并发回收,不会再次检查触发条件。
// proc.go // start forcegc helper goroutine func init() { go forcegchelper() }
// proc.go func forcegchelper() { // 保存到全局变量。 forcegc.g = getg() for { lock(&forcegc.lock) if forcegc.idle != 0 { throw("forcegc: phase error") } // 休眠,等待 sysmon 唤醒。 atomic.Store(&forcegc.idle, 1) goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceEvGoBlock, 1) // 启动并发回收。 gcStart(gcTrigger{kind: gcTriggerTime, now: nanotime()}) } }
// runtime2.go var forcegc forcegcstate type forcegcstate struct { lock mutex g *g idle uint32 }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论