2020.03.08 20:47:44 字数 354 阅读 112
SetFinalizer 设置一个 finalizer 关联到一个对象 obj,当垃圾回收准备回收 obj 的时候,它会断开这个连接,并在单独的 goroutine 中执行 finalizer(obj), 这将让 obj 再次可用,但是不会再关联 finalizer,
在 SetFinalizer 中:
先检查元素, 如果类型为 nil, 或者不为指针,元素值为 nil 都会报错
查找对象的 heap 地址
如果是函数类型为 nil, 则移除 removefinalizer
检测参数类型, 并给计算返回值 size
createfing()确保 finalizer goroutine 正在运行添加 finalizer
func SetFinalizer(obj interface{}, finalizer interface{}) {
e := efaceOf(&obj)
etyp := e.type
if etyp == nil {
throw(“runtime.SetFinalizer: first argument is nil”)
}
if etyp.kind&kindMask != kindPtr {
throw(“runtime.SetFinalizer: first argument is “ + etyp.string() + “, not pointer”)
}
ot := (*ptrtype)(unsafe.Pointer(etyp))
if ot.elem == nil {
throw(“nil elem type!”)
} ``` base, , _ := findObject(uintptr(e.data), 0, 0) f := efaceOf(&finalizer) ftyp := f._type if ftyp == nil {systemstack(func() {
removefinalizer(e.data)
}) return } fint := ft.in()[0]
createfing()
systemstack(func() { if !addfinalizer(e.data, (*funcval)(f.data), nret, fint, ot) { throw(“runtime.SetFinalizer: finalizer already set”) } })
<br />}从 mheap.specialfinalizeralloc 中申请一块内存, 然后调用`addspecial`, 如果成功则返回,失败则 free 掉刚才申请的内存<br />在`addspecial` ,根据 p 指针找到对应的 span, 轮训找到是否有相同 offset 和 kind 的数据存在,如果存在则 finalizer 函数已经存在,否则将其加入到`span.specials`中去 (这里有根据 offset 排序)
func addfinalizer(p unsafe.Pointer, f funcval, nret uintptr, fint type, ot *ptrtype) bool { lock(&mheap.speciallock) s := (*specialfinalizer)(mheap.specialfinalizeralloc.alloc()) unlock(&mheap.speciallock) if addspecial(p, &s.special) { return true }
lock(&mheap_.speciallock)mheap_.specialfinalizeralloc.free(unsafe.Pointer(s))unlock(&mheap_.speciallock)return false
} func addspecial(p unsafe.Pointer, s *special) bool { span := spanOfHeap(uintptr(p)) lock(&span.speciallock)
t := &span.specialsfor {x := *tif x == nil {break}if offset == uintptr(x.offset) && kind == x.kind {unlock(&span.speciallock)releasem(mp)return false}if offset < uintptr(x.offset) || (offset == uintptr(x.offset) && kind < x.kind) {break}t = &x.next}s.offset = uint16(offset)s.next = *t*t = sunlock(&span.speciallock)releasem(mp)return true
}
sweep 函数中,如果 span 有 special record, 先检查是否有`finalizer`, 如果没有的话则调用`freespecial`, 将其加入到`finq`<br />`var finq *finblock // list of finalizers that are to be executed`
func (s *mspan) sweep(preserve bool) bool {
specialp := &s.specialsspecial := *specialpfor special != nil {objIndex := uintptr(special.offset) / sizep := s.base() + objIndex*sizembits := s.markBitsForIndex(objIndex)if !mbits.isMarked() {hasFin := falseendOffset := p - s.base() + sizefor tmp := special; tmp != nil && uintptr(tmp.offset) < endOffset; tmp = tmp.next {if tmp.kind == _KindSpecialFinalizer {mbits.setMarkedNonAtomic()hasFin = truebreak}}for special != nil && uintptr(special.offset) < endOffset {p := s.base() + uintptr(special.offset)if special.kind == _KindSpecialFinalizer || !hasFin {y := specialspecial = special.next*specialp = specialfreespecial(y, unsafe.Pointer(p), size)} else {specialp = &special.nextspecial = *specialp}}} else {specialp = &special.nextspecial = *specialp}}
}
在`createfing`中新建了一个 goroutine 处理这个队列, 获取 finq 队列中的数据<br />如果队列为空,则休眠, findrunable 会检查唤醒,<br />否则循环,调用`reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))`执行 fin 函数
```func createfing() {
if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {go runfinq()}
}
func runfinq() { var ( frame unsafe.Pointer framecap uintptr )
for {lock(&finlock)fb := finqfinq = nilif fb == nil {gp := getg()fing = gpfingwait = truegoparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)continue}unlock(&finlock)if raceenabled {racefingo()}for fb != nil {for i := fb.cnt; i > 0; i-- {f := &fb.fin[i-1]framesz := unsafe.Sizeof((interface{})(nil)) + f.nretif framecap < framesz {frame = mallocgc(framesz, nil, true)framecap = framesz}if f.fint == nil {throw("missing type in runfinq")}*(*[2]uintptr)(frame) = [2]uintptr{}switch f.fint.kind & kindMask {case kindPtr:*(*unsafe.Pointer)(frame) = f.argcase kindInterface:ityp := (*interfacetype)(unsafe.Pointer(f.fint))(*eface)(frame)._type = &f.ot.typ(*eface)(frame).data = f.argif len(ityp.mhdr) != 0 {*(*iface)(frame) = assertE2I(ityp, *(*eface)(frame))}default:throw("bad kind in runfinq")}fingRunning = truereflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))fingRunning = falsef.fn = nilf.arg = nilf.ot = nilatomic.Store(&fb.cnt, i-1)}next := fb.nextlock(&finlock)fb.next = fincfinc = fbunlock(&finlock)fb = next}}
} ```
更多精彩内容,就在简书 APP
“我喜欢这种喧闹。 我们只有待在一起,才能克服心中的恐惧”
还没有人赞赏,支持一下
推荐阅读更多精彩内容
go 1.12.7 文中未标明包名之名称均在 runtime 包中 interface 非空接口类型 iface …
本文翻译自 Channels In Go Channel 是 Go 中一个重要的内置功能。这是让 Go 独一无二的功能之一,除…
1.gc 垃圾回收算法:标记 - 清除法 基本原理:从根(包括全局指针以及 goroutine 栈上指针)出发,标记可达节点…
Chapter 8 Goroutines and Channels Go enable two styles of…
我不曾拥有过你的夜空 星星和太阳也没有为我照亮。 我没有拥你入怀紧紧相依。 我们的爱没有痕迹可寻。 我的太阳,我的…

莫非漠阅读 210 评论 0 赞 9
https://www.jianshu.com/p/ad746957e7be


