返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

7.1 设置

发布于 2024-10-12 19:16:08 字数 2579 浏览 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. 

func SetFinalizer(obj interface{}, finalizer interface{}) {
    ...
    
    // 创建专门 goroutine,用于执行终结函数。
    createfing()
    
    // 添加。
    systemstack(func() {
        if !addfinalizer(e.data, (*funcval)(f.data), nret, fint, ot) {
            throw("runtime.SetFinalizer: finalizer already set")
        }
    })
}

addfinalizer

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

多个 special 构成链表,按地址偏移量和类型排序。

同一对象除 finalizer,还可能有 profile,都存储在该链表内。

// mheap.go

const (
    _KindSpecialFinalizer = 1
    _KindSpecialProfile   = 2
)

type specialfinalizer struct {
    special special
    fn      *funcval
    nret    uintptr
    fint    *_type
    ot      *ptrtype
}

type special struct {
    next   *special
    offset uint16
    kind   byte
}

type mspan struct {
    specials *special
}
// mheap.go

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

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 {
            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
    
    return true
}

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

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

发布评论

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