A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
包含着 defer 的函数返回时
包含着 defer 的函数执行到最后时
当前的 goroutinue 发生 panic 时
2. defer、return、返回值的执行顺序
1
if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller.
type _defer struct { siz int32// 参数和结果的内存大小 started bool heap bool // openDefer indicates that this _defer is for a frame with open-coded // defers. We have only one defer record for the entire frame (which may // currently have 0, 1, or more defers active). // 开放编码优化 openDefer bool sp uintptr// 栈指针 pc uintptr// 调用方的程序计数器 fn *funcval // 是 defer 关键字中传入的函数 _panic *_panic // 触发延迟调用的结构体,可能为空 link *_defer // 指向下一个 _defer 结构体,是一个链表结构
// If openDefer is true, the fields below record values about the stack // frame and associated function that has the open-coded defer(s). sp // above will be the sp for the frame, and pc will be address of the // deferreturn call in the function. fd unsafe.Pointer // funcdata for the function associated with the frame varp uintptr// value of varp for the stack frame // framepc is the current pc associated with the stack frame. Together, // with sp above (which is the sp associated with the stack frame), // framepc/sp can be used as pc/sp pair to continue a stack trace via // gentraceback(). framepc uintptr }
而 runtime._defer 结构体是延迟调用链表上的一个元素,所有结构体都会通过 link 字段串联成链表。
Go 语言代码在编译时,中间代码生成阶段 cmd/compile/internal/gc/ssa.go 中 stmt 会负责处理程序中的 defer ,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
func(s *state) stmt(n *Node) { ... case ODEFER: ... // 开放编码如果开启,直接使用 if s.hasOpenDefers { s.openDeferRecord(n.Left) } else { // 堆中分配 d := callDefer if n.Esc == EscNever { // 栈上分配 d = callDeferStack } s.call(n.Left, d) } ... }
func(s *state) call(n *Node, k callKind) *ssa.Value { ... var call *ssa.Value if k == callDeferStack { ... } else { // call target switch { case k == callDefer: call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem()) ... } call.AuxInt = stksize // Call operations carry the argsize of the callee along with them } s.vars[&memVar] = call ... }
funcdeferproc(siz int32, fn *funcval) { // arguments of fn follow fn gp := getg() if gp.m.curg != gp { // go code on the system stack can't defer throw("defer on system stack") }
// the arguments of fn are in a perilous state. The stack map // for deferproc does not describe them. So we can't let garbage // collection or stack copying trigger until we've copied them out // to somewhere safe. The memmove below does that. // Until the copy completes, we can only call nosplit routines. sp := getcallersp() argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn) callerpc := getcallerpc() // 获得 runtime._defer 结构体 d := newdefer(siz) if d._panic != nil { throw("deferproc: d.panic != nil after newdefer") } d.link = gp._defer gp._defer = d d.fn = fn d.pc = callerpc d.sp = sp switch siz { case0: // Do nothing. case sys.PtrSize: *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp)) default: memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz)) }
funcnewdefer(siz int32) *_defer { var d *_defer sc := deferclass(uintptr(siz)) gp := getg() if sc < uintptr(len(p{}.deferpool)) { pp := gp.m.p.ptr() // 从调度器的延迟调用缓存池中取出结构体,并且将其追加到当前 Goroutinue 的缓存池中 iflen(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil { // Take the slow path on the system stack so // we don't grow newdefer's stack. systemstack(func() { lock(&sched.deferlock) forlen(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) }) } 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 { // Allocate new defer+args. // 在堆上创建 _defer 结构体 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.heap = true return d }
// cmd/compile/internal/gc/ssa.go func(s *state) call(n *Node, k callKind) *ssa.Value { ... var call *ssa.Value if k == callDeferStack { // 在栈上创建 _defer 结构体 t := deferstruct(stksize) ... // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) // 调用 deferprocStack call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. stksize = int64(Widthptr) } call.AuxInt = stksize } ... }
// go/src/cmd/compile/internal/gc/ssa.go const maxOpenDefers = 8 // go/src/cmd/compile/internal/gc/walk.go funcwalkstmt(n *Node) *Node { ... switch n.Op { ... case ODEFER: Curfn.Func.SetHasDefer(true) Curfn.Func.numDefers++ // 如果大于 8 个,则禁用开放编码模式 if Curfn.Func.numDefers > maxOpenDefers { Curfn.Func.SetOpenCodedDeferDisallowed(true) } // 处于循环中,则禁用开放编码模式 if n.Esc != EscNever { // If n.Esc is not EscNever, then this defer occurs in a loop, // so open-coded defers cannot be used in this function. Curfn.Func.SetOpenCodedDeferDisallowed(true) } fallthrough ... } ... }
// go/src/cmd/compile/internal/gc/ssa.go funcbuildssa(fn *Node, worker int) *ssa.Func { ... if s.hasOpenDefers { // Create the deferBits variable and stack slot. deferBits is a // bitmask showing which of the open-coded defers in this function // have been activated. deferBitsTemp := tempAt(src.NoXPos, s.curfn, types.Types[TUINT8]) s.deferBitsTemp = deferBitsTemp // For this value, AuxInt is initialized to zero by default startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[TUINT8]) s.vars[&deferBitsVar] = startDeferBits s.deferBitsAddr = s.addr(deferBitsTemp) s.store(types.Types[TUINT8], s.deferBitsAddr, startDeferBits) // Make sure that the deferBits stack slot is kept alive (for use // by panics) and stores to deferBits are not eliminated, even if // all checking code on deferBits in the function exit can be // eliminated, because the defer statements were all // unconditional. s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false) } ... }
// go/src/cmd/compile/internal/gc/ssa.go // 传入 defer 关键字的函数和参数都会存储在 openDeferInfo 结构体 type openDeferInfo struct { // The ODEFER node representing the function call of the defer n *Node // If defer call is closure call, the address of the argtmp where the // closure is stored. // 存储着调用的函数 closure *ssa.Value // The node representing the argtmp where the closure is stored - used for // function, method, or interface call, to store a closure that panic // processing can use for this defer. closureNode *Node // If defer call is interface call, the address of the argtmp where the // receiver is stored // 存储方法的接收者 rcvr *ssa.Value // The node representing the argtmp where the receiver is stored rcvrNode *Node // The addresses of the argtmps where the evaluated arguments of the defer // function call are stored. // 存储着函数的参数 argVals []*ssa.Value // The nodes representing the argtmps where the args of the defer are stored argNodes []*Node }