上卷 程序设计
中卷 标准库
- 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 触发
除因为内存分配触发阈值引起回收外,sysmon 和 runtime.GC 调用也可以启动。
// mgc.go const ( // 达到触发阈值。(mallocgc) gcTriggerHeap gcTriggerKind = iota // 长时间没有启动垃圾回收,强制执行。(sysmon) gcTriggerTime // 手工调用 GC 启动一次回收。(runtime.GC) gcTriggerCycle )
mallocgc
分配内存时,并非每次都会检查。
只在大尺寸内存分配时才会进行(shouldhelpgc = true),比如扩容、大对象等。
// malloc.go func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { shouldhelpgc := false if size <= maxSmallSize { // 小对象。 v, span, shouldhelpgc = c.nextFree(spc) } else { // 大对象。 shouldhelpgc = true span = largeAlloc(size, needzero, noscan) } if shouldhelpgc { if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { gcStart(t) } } return x }
// 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 } return }
启动前需要检查相关条件。
// mgc.go func (t gcTrigger) test() bool { // 不允许启动的状态。 if !memstats.enablegc || panicking != 0 || gcphase != _GCoff { return false } switch t.kind { case gcTriggerHeap: // 检查是否突破阈值。 return memstats.heap_live >= memstats.gc_trigger case gcTriggerTime: if gcpercent < 0 { return false } // 检查时间差,确认是否长时间(2 分钟)没有启动回收。 lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime)) return lastgc != 0 && t.now-lastgc > forcegcperiod case gcTriggerCycle: // 避免在回收进行中再次启动。 // 调用 runtime.GC 会递增 t.n,随后回收周期启动时 work.cycles 会递增。 // 如果相等,则表示已经进入回收标记阶段,不能再次启动。 return int32(t.n-work.cycles) > 0 } 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. var forcegcperiod int64 = 2 * 60 * 1e9
runtime.GC
在完整清理周期结束前,调用该函数会等待。
1. 如果当前正在执行标记,那么等待此次(work.cycles = N)标记结束。
2. 启动 N + 1 次回收周期。 <-- 这是本次调用要启动的回收周期, work.cycles++
在 gcStart 中。
3. 等待 N + 1 次标记结束。
4. 等待 N + 1 次清理结束。
// 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() { n := atomic.Load(&work.cycles) // 等待已启动的第 N 次循环标记结束。 gcWaitOnMark(n) // 启动 N + 1 次循环。 gcStart(gcTrigger{kind: gcTriggerCycle, n: n + 1}) // 等待 N + 1 次标记结束。 gcWaitOnMark(n + 1) // 等待 N + 1 次清理结束。 for atomic.Load(&work.cycles) == n+1 && sweepone() != ^uintptr(0) { Gosched() } }
sysmon, forcegc
如果长时间没有执行过回收,那么强制启动。
触发回收操作的是一个专门的 goroutine。它一直处于休眠循环中,等待唤醒。
从条件测试函数看,强制回收不检查阈值。
// proc.go // start forcegc helper goroutine func init() { go forcegchelper() }
// proc.go func forcegchelper() { forcegc.g = getg() for { // 休眠,等待 sysmon 唤醒。 atomic.Store(&forcegc.idle, 1) goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceEvGoBlock, 1) // M 停止执行该 G,且不放回队列。 // 启动回收。 gcStart(gcTrigger{kind: gcTriggerTime, now: nanotime()}) } }
// runtime2.go var forcegc forcegcstate type forcegcstate struct { lock mutex g *g idle uint32 }
所谓唤醒,就是将 forcegc goroutine 重新放回任务队列。
// proc.go func sysmon() { for { usleep(delay) // 检查是否要强制执行回收。 if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { // 重新放回任务队列,等待唤醒执行。 forcegc.idle = 0 var list gList list.push(forcegc.g) injectglist(&list) // 将列表中的 G 全部放入全局待运行队列。 } } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论