返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

7.2 队列

发布于 2024-10-12 19:16:00 字数 3850 浏览 0 评论 0 收藏 0

在清理 span 时,检查终结器,将终结函数打包成 finalizer 放到待执行队列。

// mgcsweep.go

// Sweep frees or collects finalizers for blocks not marked in the mark phase.
func (sl *sweepLocked) sweep(preserve bool) bool {

    s := sl.mspan
	spc := s.spanclass
	size := s.elemsize

	hadSpecials := s.specials != nil
	siter := newSpecialsIter(s)
    
    // 迭代 mspan.specials 上所有终结器。
	for siter.valid() {
        
        // 计算所关联对象实际地址。
		objIndex := uintptr(siter.s.offset) / size
		p := s.base() + objIndex*size
        
        // 检查扫描标记。如可回收,则可执行终结函数。
		mbits := s.markBitsForIndex(objIndex)
		if !mbits.isMarked() {
            
            // 未标记,表示可回收。
            
			// 1: 看看是否有终结器。
			hasFin := false
			endOffset := p - s.base() + size
            
            // 因为 siter.s 可能是当前关联对象的 special 或 profile,
            // 所以要循环检查 kind,直到下个对象(offset > endOffset)。
			for tmp := siter.s; tmp != nil && uintptr(tmp.offset) < endOffset; tmp = tmp.next {
				if tmp.kind == _KindSpecialFinalizer {
                    
                    // 因为有终结器要执行,所以重新激活对象(标记,下次回收)。
					mbits.setMarkedNonAtomic()
                    
					hasFin = true
					break
				}
			}
            
			// 2: 放入待运行队列。
			for siter.valid() && uintptr(siter.s.offset) < endOffset {
				special := siter.s
				p := s.base() + uintptr(special.offset)
                
				if special.kind == _KindSpecialFinalizer || !hasFin {
                    // 从链表移除。
					siter.unlinkAndNext()
                    
                    // 释放 special,并将终结函数加入待运行队列。
					freeSpecial(special, unsafe.Pointer(p), size)
				} else {
					siter.next()
				}
			}
		} else {
			// object is still live
			if siter.s.kind == _KindSpecialReachable {
                ...
			} else {
				// keep special record
				siter.next()
			}
		}
	}

    ...

	return false
}
// mheap.go

// freeSpecial performs any cleanup on special s and deallocates it.
// s must already be unlinked from the specials list.

func freeSpecial(s *special, p unsafe.Pointer, size uintptr) {
	switch s.kind {
	case _KindSpecialFinalizer:
        
        // 将终结函数加入队列。
		sf := (*specialfinalizer)(unsafe.Pointer(s))
		queuefinalizer(p, sf.fn, sf.nret, sf.fint, sf.ot)
        
        // 释放。
		mheap_.specialfinalizeralloc.free(unsafe.Pointer(sf))
        
	case _KindSpecialProfile: ...
	case _KindSpecialReachable: ...
	default:
		throw("bad special kind")
		panic("not reached")
	}
}

待执行队列 finq,由多个 finblock 构成链表。每个 finblock 用数组存储一批 finalizer 对象。

// mfinal.go

var finq *finblock // list of finalizers that are to be executed
var finc *finblock // cache of free blocks


type finblock struct {
	alllink *finblock
	next    *finblock
	cnt     uint32
	_       int32
	fin     [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
}

type finalizer struct {
	fn   *funcval       // function to call (may be a heap pointer)
	arg  unsafe.Pointer // ptr to object (may be a heap pointer)
	nret uintptr        // bytes of return values from fn
	fint *_type         // type of first argument of fn
	ot   *ptrtype       // type of ptr to object (may be a heap pointer)
}
// mfinal.go

func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot *ptrtype) {
    
    // 队列未分配或已满,重新申请。
	if finq == nil || finq.cnt == uint32(len(finq.fin)) {
        
        // 缓存为空,分配。
		if finc == nil {
			finc = (*finblock)(persistentalloc(_FinBlockSize, 0, &memstats.gcMiscSys))
			finc.alllink = allfin
			allfin = finc
		}

        // 从缓存取一个 block 加入 finq 链表头部。
		block := finc
		finc = block.next
		block.next = finq
		finq = block
	}
    
    // 取一个空位。
	f := &finq.fin[finq.cnt]
	atomic.Xadd(&finq.cnt, +1)
    
    // 设置属性。
	f.fn = fn
	f.nret = nret
	f.fint = fint
	f.ot = ot
	f.arg = p
    
    // 唤醒标记。
	fingwake = true
}

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

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

发布评论

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