返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.6.6 垃圾回收

发布于 2024-10-12 19:15:59 字数 2941 浏览 0 评论 0 收藏 0

在垃圾标记的 markroot 里,引发 dead Gs 栈内存回收操作。

// mgcmark.go

func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 {
    switch {
    case i == fixedRootFreeGStacks:
        systemstack(markrootFreeGStacks)
    }
}
// mgcmark.go

// 释放 dead Gs 栈内存。
func markrootFreeGStacks() {
    
	// Take list of dead Gs with stacks.
	list := sched.gFree.stack
	sched.gFree.stack = gList{}
	if list.empty() {
		return
	}

	// Free stacks.
	q := gQueue{list.head, list.head}
	for gp := list.head.ptr(); gp != nil; gp = gp.schedlink.ptr() {
        
		stackfree(gp.stack)
		gp.stack.lo = 0
		gp.stack.hi = 0
        
		// Manipulate the queue directly since the Gs are
		// already all linked the right way.
		q.tail.set(gp)
	}

	// Put Gs back on the free list.
	sched.gFree.noStack.pushAll(q)
}

其次,在垃圾扫描结束时,释放 stackpool 内存块,回收 mcache.stackcache 缓存。

// mgc.go

func gcMarkTermination(nextTriggerRatio float64) {
    systemstack(freeStackSpans)
    
    systemstack(func() {
		forEachP(func(_p_ *p) {
			_p_.mcache.prepareForSweep()  // stackcache_clear(c)
		})
	})
}
// stack.go

// freeStackSpans frees unused stack spans at the end of GC.
func freeStackSpans() {
    
	// 池内小块。
	for order := range stackpool {
		list := &stackpool[order].item.span
		for s := list.first; s != nil; {
			next := s.next
            
            // 内存全部收回。
			if s.allocCount == 0 {
                
                // 从链表移除。
				list.remove(s)
				s.manualFreeList = 0
                
                // 释放。
				osStackFree(s)
				mheap_.freeManual(s, spanAllocStack)
			}
            
			s = next
		}
	}

	// 大块。
	for i := range stackLarge.free {
		for s := stackLarge.free[i].first; s != nil; {
			next := s.next
            
            // 从链表移除。
			stackLarge.free[i].remove(s)
			
            // 释放。
            osStackFree(s)
			mheap_.freeManual(s, spanAllocStack)
			
            s = next
		}
	}
}
// mcache.go, stack.go

func (c *mcache) prepareForSweep() {
	stackcache_clear(c)
}

func stackcache_clear(c *mcache) {
	for order := uint8(0); order < _NumStackOrders; order++ {
		x := c.stackcache[order].list
		for x.ptr() != nil {
			y := x.ptr().next
			stackpoolfree(x, order)
			x = y
		}
        
		c.stackcache[order].list = 0
		c.stackcache[order].size = 0
	}
}

最后,对 G 进行栈内存收缩。

// mgcmark.go

func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 {
    switch {
    default:
        scanstack(gp, gcw)
    }
}

func scanstack(gp *g, gcw *gcWork) int64 {
    shrinkstack(gp)
}
// stack.go

func shrinkstack(gp *g) {

    // 1/2
    oldsize := gp.stack.hi - gp.stack.lo
	newsize := oldsize / 2
    
	if newsize < _FixedStack {
		return
	}
    
    // 已用空间不足 1/4 时才收缩。
	avail := gp.stack.hi - gp.stack.lo
	if used := gp.stack.hi - gp.sched.sp + _StackLimit; used >= avail/4 {
		return
	}

    // 分配新栈(小),并复制数据。
	copystack(gp, newsize)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文