golang 源码剖析(8): 析构SetFinalizer - 简书 - 图1

2020.03.08 20:47:44 字数 354 阅读 112

SetFinalizer 设置一个 finalizer 关联到一个对象 obj,当垃圾回收准备回收 obj 的时候,它会断开这个连接,并在单独的 goroutine 中执行 finalizer(obj), 这将让 obj 再次可用,但是不会再关联 finalizer,
在 SetFinalizer 中:

  1. 先检查元素, 如果类型为 nil, 或者不为指针,元素值为 nil 都会报错

  2. 查找对象的 heap 地址

  3. 如果是函数类型为 nil, 则移除 removefinalizer

  4. 检测参数类型, 并给计算返回值 size

  5. createfing()确保 finalizer goroutine 正在运行

  6. 添加 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() {

    1. 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”) } })

  1. <br />}
  2. 从 mheap.specialfinalizeralloc 中申请一块内存, 然后调用`addspecial`, 如果成功则返回,失败则 free 掉刚才申请的内存<br />
  3. 在`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 }

  1. lock(&mheap_.speciallock)
  2. mheap_.specialfinalizeralloc.free(unsafe.Pointer(s))
  3. unlock(&mheap_.speciallock)
  4. return false

} func addspecial(p unsafe.Pointer, s *special) bool { span := spanOfHeap(uintptr(p)) lock(&span.speciallock)

  1. t := &span.specials
  2. for {
  3. x := *t
  4. if x == nil {
  5. break
  6. }
  7. if offset == uintptr(x.offset) && kind == x.kind {
  8. unlock(&span.speciallock)
  9. releasem(mp)
  10. return false
  11. }
  12. if offset < uintptr(x.offset) || (offset == uintptr(x.offset) && kind < x.kind) {
  13. break
  14. }
  15. t = &x.next
  16. }
  17. s.offset = uint16(offset)
  18. s.next = *t
  19. *t = s
  20. unlock(&span.speciallock)
  21. releasem(mp)
  22. return true

}

  1. sweep 函数中,如果 span special record, 先检查是否有`finalizer`, 如果没有的话则调用`freespecial`, 将其加入到`finq`<br />
  2. `var finq *finblock // list of finalizers that are to be executed`

func (s *mspan) sweep(preserve bool) bool {

  1. specialp := &s.specials
  2. special := *specialp
  3. for special != nil {
  4. objIndex := uintptr(special.offset) / size
  5. p := s.base() + objIndex*size
  6. mbits := s.markBitsForIndex(objIndex)
  7. if !mbits.isMarked() {
  8. hasFin := false
  9. endOffset := p - s.base() + size
  10. for tmp := special; tmp != nil && uintptr(tmp.offset) < endOffset; tmp = tmp.next {
  11. if tmp.kind == _KindSpecialFinalizer {
  12. mbits.setMarkedNonAtomic()
  13. hasFin = true
  14. break
  15. }
  16. }
  17. for special != nil && uintptr(special.offset) < endOffset {
  18. p := s.base() + uintptr(special.offset)
  19. if special.kind == _KindSpecialFinalizer || !hasFin {
  20. y := special
  21. special = special.next
  22. *specialp = special
  23. freespecial(y, unsafe.Pointer(p), size)
  24. } else {
  25. specialp = &special.next
  26. special = *specialp
  27. }
  28. }
  29. } else {
  30. specialp = &special.next
  31. special = *specialp
  32. }
  33. }

}

  1. `createfing`中新建了一个 goroutine 处理这个队列, 获取 finq 队列中的数据<br />
  2. 如果队列为空,则休眠, findrunable 会检查唤醒,<br />
  3. 否则循环,调用`reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))`执行 fin 函数

```func createfing() {

  1. if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
  2. go runfinq()
  3. }

}

func runfinq() { var ( frame unsafe.Pointer framecap uintptr )

  1. for {
  2. lock(&finlock)
  3. fb := finq
  4. finq = nil
  5. if fb == nil {
  6. gp := getg()
  7. fing = gp
  8. fingwait = true
  9. goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)
  10. continue
  11. }
  12. unlock(&finlock)
  13. if raceenabled {
  14. racefingo()
  15. }
  16. for fb != nil {
  17. for i := fb.cnt; i > 0; i-- {
  18. f := &fb.fin[i-1]
  19. framesz := unsafe.Sizeof((interface{})(nil)) + f.nret
  20. if framecap < framesz {
  21. frame = mallocgc(framesz, nil, true)
  22. framecap = framesz
  23. }
  24. if f.fint == nil {
  25. throw("missing type in runfinq")
  26. }
  27. *(*[2]uintptr)(frame) = [2]uintptr{}
  28. switch f.fint.kind & kindMask {
  29. case kindPtr:
  30. *(*unsafe.Pointer)(frame) = f.arg
  31. case kindInterface:
  32. ityp := (*interfacetype)(unsafe.Pointer(f.fint))
  33. (*eface)(frame)._type = &f.ot.typ
  34. (*eface)(frame).data = f.arg
  35. if len(ityp.mhdr) != 0 {
  36. *(*iface)(frame) = assertE2I(ityp, *(*eface)(frame))
  37. }
  38. default:
  39. throw("bad kind in runfinq")
  40. }
  41. fingRunning = true
  42. reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
  43. fingRunning = false
  44. f.fn = nil
  45. f.arg = nil
  46. f.ot = nil
  47. atomic.Store(&fb.cnt, i-1)
  48. }
  49. next := fb.next
  50. lock(&finlock)
  51. fb.next = finc
  52. finc = fb
  53. unlock(&finlock)
  54. fb = next
  55. }
  56. }

} ```

更多精彩内容,就在简书 APP

“我喜欢这种喧闹。 我们只有待在一起,才能克服心中的恐惧”

还没有人赞赏,支持一下

golang 源码剖析(8): 析构SetFinalizer - 简书 - 图2

推荐阅读更多精彩内容

  • 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…

  • 我不曾拥有过你的夜空 星星和太阳也没有为我照亮。 我没有拥你入怀紧紧相依。 我们的爱没有痕迹可寻。 我的太阳,我的…
    golang 源码剖析(8): 析构SetFinalizer - 简书 - 图3
    莫非漠
    阅读 210 评论 0 赞 9
    https://www.jianshu.com/p/ad746957e7be