返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

7.1 设置

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

为对象设置关联终结函数(finalizer)。

// mfinal.go

// SetFinalizer sets the finalizer associated with obj to the provided
// finalizer function. When the garbage collector finds an unreachable block
// with an associated finalizer, it clears the association and runs
// finalizer(obj) in a separate goroutine. This makes obj reachable again,
// but now without an associated finalizer. Assuming that SetFinalizer
// is not called again, the next time the garbage collector sees
// that obj is unreachable, it will free obj.

func SetFinalizer(obj any, finalizer any) {
    
    ...
    
    // 创建专门 goroutine,用于执行终结函数。
	createfing()

    // 添加。
	systemstack(func() {
		if !addfinalizer(e.data, (*funcval)(f.data), nret, fint, ot) {
			throw("runtime.SetFinalizer: finalizer already set")
		}
	})
}

添加

将相关信息打包,添加到所属 span.specials 链表内。

多个 special 构成链表,按地址偏移量和类型排序。
同一对象除 finalizer,还可能有 profile,都存储在该链表内。​

// mheap.go

type special struct {
	next   *special // linked list in span
	offset uint16   // span offset of object
	kind   byte     // kind of special
}

// The described object has a finalizer set for it.
type specialfinalizer struct {
	special special
	fn      *funcval
	nret    uintptr
	fint    *_type  
	ot      *ptrtype
}
// mheap.go

type mspan struct {
    specials    *special  // linked list of special records sorted by offset.
}

const (
	_KindSpecialFinalizer = 1
	_KindSpecialProfile   = 2
)

无法为同一目标对象添加多个终结器,即便终结函数不同。

// mheap.go

// Adds a finalizer to the object p. Returns true if it succeeded.
func addfinalizer(p unsafe.Pointer, f *funcval, nret uintptr, fint *_type, ot *ptrtype) bool {

    // 以 fixalloc 分配。
	s := (*specialfinalizer)(mheap_.specialfinalizeralloc.alloc())
	s.special.kind = _KindSpecialFinalizer
	s.fn = f
	s.nret = nret
	s.fint = fint
	s.ot = ot
    
    // 添加。
	if addspecial(p, &s.special) {
		return true
	}

	// 添加失败,释放。
	mheap_.specialfinalizeralloc.free(unsafe.Pointer(s))
	return false
}
// mheap.go

// Adds the special record s to the list of special records for
// the object p. All fields of s should be filled in except for
// offset & next, which this routine will fill in.
// Returns true if the special was successfully added, false otherwise.
// (The add will fail only if a record with the same p and s->kind
//  already exists.)

func addspecial(p unsafe.Pointer, s *special) bool {
    
    // 找到被关联对象所属 span,计算其地址偏移量。
	span := spanOfHeap(uintptr(p))
	offset := uintptr(p) - span.base()
    
	kind := s.kind

    // 循环链表,找到合适位置。(维持链表有序状态)
	t := &span.specials
	for {
		x := *t
		if x == nil {
			break
		}
        
        // 所关联目标相同,添加失败。
		if offset == uintptr(x.offset) && kind == x.kind {
			releasem(mp)
			return false // already exists
		}
        
        // 按地址偏移量从小到大。
		if offset < uintptr(x.offset) || (offset == uintptr(x.offset) && kind < x.kind) {
			break
		}
		t = &x.next
	}

	// 加入链表。
	s.offset = uint16(offset)
	s.next = *t
	*t = s
    
    // 标记位图。
	spanHasSpecials(span)

	return true
}

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

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

发布评论

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