Go-深入理解Defer

深入理解Defer

defer

defer函数的参数在声明之前,其实已经确定了值

defer 结构体

	type _defer struct {
		siz     int32   // 参数的大小
		started bool    // 是否执行过了
		sp      uintptr // 函数栈指针
		pc      uintptr 
		fn      *funcval 
		_panic  *_panic // defer中的panic
		link    *_defer // 通过链表的形势存储_defer
	}

deferproc


func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
	if getg().m.curg != getg() {
		// go code on the system stack can't defer
		throw("defer on system stack")
	}

	sp := getcallersp()
	argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
	callerpc := getcallerpc()
	// 新建一个defer
	d := newdefer(siz)
	if d._panic != nil {
		throw("deferproc: d.panic != nil after newdefer")
	}
	// 设置defer信息
	d.fn = fn
	d.pc = callerpc
	d.sp = sp
	switch siz {
	case 0:
		// Do nothing.
	case sys.PtrSize:
		// 如果是指针,_defer后面内存存储参数地址信息
		*(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
	default:
		// 如果不是指针类型,把参数拷贝到_defer后面
		memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
	}

	// deferproc returns 0 normally.
	// a deferred func that stops a panic
	// makes the deferproc return 1.
	// the code the compiler generates always
	// checks the return value and jumps to the
	// end of the function if deferproc returns != 0.
	return0()
	// No code can go here - the C return register has
	// been set and must not be clobbered.
}

newdefer

newdefer 是获取一个_defer对象,并把它加入到链表头部

func newdefer(siz int32) *_defer {
	var d *_defer
	// 根据size通过deferclass得出应该分配的sizeclass,系统会预先分配好一些sizeclass,然后根据对应的sz,去直接读取缓存
	sc := deferclass(uintptr(siz))
	gp := getg()
	// 在当前p的范围内,去p上找
	if sc < uintptr(len(p{}.deferpool)) {
		pp := gp.m.p.ptr()
		if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil {
			// 当前p上对应sc的缓存为0,则从sched上获取一批
			systemstack(func() {
				lock(&sched.deferlock)
				for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil {
					d := sched.deferpool[sc]
					sched.deferpool[sc] = d.link
					d.link = nil
					pp.deferpool[sc] = append(pp.deferpool[sc], d)
				}
				unlock(&sched.deferlock)
			})
		}
		// 从sched获取到了,并且不为空,则分配
		if n := len(pp.deferpool[sc]); n > 0 {
			d = pp.deferpool[sc][n-1]
			pp.deferpool[sc][n-1] = nil
			pp.deferpool[sc] = pp.deferpool[sc][:n-1]
		}
	}
	if d == nil {
		// 如果还没找到,则直接分配
		systemstack(func() {
			total := roundupsize(totaldefersize(uintptr(siz)))
			d = (*_defer)(mallocgc(total, deferType, true))
		})
		if debugCachedWork {
			// Duplicate the tail below so if there's a
			// crash in checkPut we can tell if d was just
			// allocated or came from the pool.
			d.siz = siz
			d.link = gp._defer
			gp._defer = d
			return d
		}
	}
	d.siz = siz
	// 插入链表头部
	d.link = gp._defer
	gp._defer = d
	return d
}

deferreturn

func deferreturn(arg0 uintptr) {
	gp := getg()
	// 获取最后一个声明的defer
	d := gp._defer
	if d == nil {
		return
	}
	sp := getcallersp()
	if d.sp != sp {
		return
	}

	// Moving arguments around.
	//
	// Everything called after this point must be recursively
	// nosplit because the garbage collector won't know the form
	// of the arguments until the jmpdefer can flip the PC over to
	// fn.
	switch d.siz {
	case 0:
		// Do nothing.
	case sys.PtrSize:
		*(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d))
	default:
		memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz))
	}
	fn := d.fn
	d.fn = nil
	// defer用完了就盛放
	gp._defer = d.link
	// 释放defer
	freedefer(d)
	// 跳转执行其他defer
	jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
}

freedefer

释放defer

func freedefer(d *_defer) {

	if d._panic != nil {
		freedeferpanic()
	}
	if d.fn != nil {
		freedeferfn()
	}
	// 判断sizeclass
	sc := deferclass(uintptr(d.siz))
	// 超过当前deferpool的范围,则直接返回
	if sc >= uintptr(len(p{}.deferpool)) {
		return
	}
	pp := getg().m.p.ptr()
	// 本地容量已经满了
	if len(pp.deferpool[sc]) == cap(pp.deferpool[sc]) {
		// 转移一半到sched
		systemstack(func() {
			var first, last *_defer
			for len(pp.deferpool[sc]) > cap(pp.deferpool[sc])/2 {
				n := len(pp.deferpool[sc])
				d := pp.deferpool[sc][n-1]
				pp.deferpool[sc][n-1] = nil
				pp.deferpool[sc] = pp.deferpool[sc][:n-1]
				if first == nil {
					first = d
				} else {
					last.link = d
				}
				last = d
			}
			lock(&sched.deferlock)
			last.link = sched.deferpool[sc]
			sched.deferpool[sc] = first
			unlock(&sched.deferlock)
		})
	}

	// 清空defer数据
	d.siz = 0
	d.started = false
	d.sp = 0
	d.pc = 0
	d.link = nil

	pp.deferpool[sc] = append(pp.deferpool[sc], d)
}

说明

从上面可以看出,defer函数的参数在defer语句的时候已经定义下来了,从下面几个例子我们着重说明

Example1

func demo1() int {
  	x := 1
  	defer func() {
		x++ 
	}()
	return x 
}

先看第一个例子,这个应该输出什么? 答案是1,为什么会输出1,我们试着转换一下,是不是就很清晰了,最后返回的起始一个已经被赋值后的变量tmp,跟x就没关系了

func demo1() int {
	x := 1
	tmp := x
	x++
	return tmp
}

Example2

func demo2() (x int) {
	x=1
  	defer func() {
    	x++
	}()
	return 
}

第二个例子输出什么呢? 如果你能马上答出2,那么说明你已经完全理解第一个例子了,那么我们还是翻译一下, 如果这还不明白,请面壁思过下

func demo2() (x int) {
	x=1
  	x++
	return 
}

Example3

func demo3() (y int) {
	x := 1
  	defer func() {
    	x++
	}()
	return x 
}

第三个例子呢, 输出是1,其实第三个和第一个是一种情况,只不过有一些误导性,再看下第一个例子就会明白了


2021-06-27 09:20 +0800