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.specials
for {
x := *t
if 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 = s
unlock(&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.specials
special := *specialp
for special != nil {
objIndex := uintptr(special.offset) / size
p := s.base() + objIndex*size
mbits := s.markBitsForIndex(objIndex)
if !mbits.isMarked() {
hasFin := false
endOffset := p - s.base() + size
for tmp := special; tmp != nil && uintptr(tmp.offset) < endOffset; tmp = tmp.next {
if tmp.kind == _KindSpecialFinalizer {
mbits.setMarkedNonAtomic()
hasFin = true
break
}
}
for special != nil && uintptr(special.offset) < endOffset {
p := s.base() + uintptr(special.offset)
if special.kind == _KindSpecialFinalizer || !hasFin {
y := special
special = special.next
*specialp = special
freespecial(y, unsafe.Pointer(p), size)
} else {
specialp = &special.next
special = *specialp
}
}
} else {
specialp = &special.next
special = *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 := finq
finq = nil
if fb == nil {
gp := getg()
fing = gp
fingwait = true
goparkunlock(&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.nret
if 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.arg
case kindInterface:
ityp := (*interfacetype)(unsafe.Pointer(f.fint))
(*eface)(frame)._type = &f.ot.typ
(*eface)(frame).data = f.arg
if len(ityp.mhdr) != 0 {
*(*iface)(frame) = assertE2I(ityp, *(*eface)(frame))
}
default:
throw("bad kind in runfinq")
}
fingRunning = true
reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
fingRunning = false
f.fn = nil
f.arg = nil
f.ot = nil
atomic.Store(&fb.cnt, i-1)
}
next := fb.next
lock(&finlock)
fb.next = finc
finc = fb
unlock(&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