defer 源码分析

一、defer 原则

首先defer 后面通常只会跟一个函数调用, 这个函数调用主要是下面这种:

defer func() {
	fmt.Println("start")
}()

假如在这个函数中出现了一个变量, 那么我们需要分析这个变量的位置

  1. 如果是在函数参数的位置, 那么这个变量会被复制:
func test5() {
	i := 1
	defer func(i int) {
		i += 1
	}(i)
}
  1. 如果变量位于函数体中的, 那么会分析这个变量从哪一个地方来
func test4() (r int) {
	defer func() {
		r = r + 5
	}()
	r = 10
	return r
}

所以它的实际过程应该是下面这种:

func test4() int {
	r = 10
	func() {
		r = r + 5
	}()
	
	return 15
}

二、defer 源码

在Go 语言中多个 defer 形成一个链表. defer 语句会首先调用一个 defer proc 函数, new 一个对应的结构体挂载到对应的G 上面调用new 之前会从 G 所绑定的 P 的 defer pool 里面取, 没有取到会从全局的defer pool里取, 实在没有的话才新建一个。这是 Go runtime 里非常常见的操作,即设置多级缓存,提升运行效率

在执行 RET 指令之前(注意不是 return 之前),调用 defer return 函数完成 _defer 链表的遍历,执行完这条链上所有被 defered 的函数(如关闭文件、释放连接、释放锁资源等)。在 deferreturn 函数的最后,会使用 jmpdefer 跳转到之前被 defered 的函数,这时控制权从 runtime 转移到了用户自定义的函数。这只是执行了一个被 defered 的函数,那这条链上其他的被 defered 的函数, 如何得到执行?

在这里插入图片描述

func example() {
    defer func() {
        fmt.Println("第二个 defer")
    }()
    defer func() {
        fmt.Println("第一个 defer")
    }()
    // 其他代码...
}
  1. 当函数 example 即将返回时,Go运行时会开始执行_defer链表中的函数。
  2. 首先执行的是“第二个 defer”,因为它是最后被添加到链表中的。
  3. 在“第二个 defer”执行完毕后,jmpdefer 会被调用来将控制权返回到Go运行时的 deferreturn 函数。
  4. deferreturn 函数接着会执行_defer链表中的下一个函数,也就是“第一个 defer”。
TEXT runtime·gogo(SB), NOSPLIT, $16-8

最后一个参数是$16-8 函数的参数信息, 这个信息是给函数调用者看的, 函数调用者可以根据这些信息去构造自己的栈帧大小
具体的执行过程如下:

  1. 确定调用函数的参数大小
  2. 执行 call 指令 ----> 确定调用函数的执行位置
  3. 确定 call 指令返回的地址
  4. 确定当前函数的地址 ----> 之后方便恢复对应的栈
  5. 执行子函数

defer 函数调用执行过程:

func deferreturn() {
	var p _panic
	p.deferreturn = true

    //getcallerpc()的值是调用defer_return函数的程序计数器
    //getcallersp()函数返回调用deferreturn函数的函数的栈指针(SP)值。
	p.start(getcallerpc(), unsafe.Pointer(getcallersp()))
	for {
		//下一份defer
        fn, ok := p.nextDefer()
		if !ok {
			break
		}
		fn()
	}
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值