说明
在Go语言中提供了一个非常有用并且好用的语法糖就是defer关键字,这个关键字的作用是:为了在方法执行完成后执行一些操作,通常这些操作应该是释放一些IO资源,比如:文件IO,网络IO等,主要是为了防止一些IO资源没有被释放从而导致内存泄漏。
另外,我们也可以借助defer做一些另外的操作,比如记录一些日志,处理panic等。
defer的本质
defer语法糖被编译后,会将defer后面的指定的func编译为以下结构体(摘自go源码)
type _defer struct {
// 略去部分代码
...
// 主要代码如下:
// fn字段指向defer关键字后定义的func地址
fn *funcval
// link指向下一个defer
link *_defer
}
从源代码中我们暂时挑选两个比较容易看懂的代码来解释:
1.fn字段: 这个字段保存的地址就是defer关键字之后定义的func
2. link字段: 这个字段指向下一个defer的定义,通过这个关键字这也就说明了defer定义的func执行顺序是按照链表的顺序执行的。
defer的执行顺序
通过下面这段简单的代码,我们来探究一下以下多个defer是按照怎么样的顺序被执行的。
package main
import "log"
func main() {
// 下面两个defer的参数为不同类型,是为了方便后面的调式更清晰
defer log.Println(1)
defer log.Println("defer")
}
探究过程:
-
首先我们在两个defer上分别设置断点,并启动调试
下面图片中我们可以看到,此时_defer参数为nil
2. 然后执行第一条defer
我们观察红框中的三个值
3. 最后执行第二条defer
通过红框中的值,细心的你应该发现了,此时_defer链表中第二个defer定义的func处于链表的第一个元素,如果main函数执行完成后,defer被调用时,遍历_defer链表,那么最先执行的肯定是第二个defer定义的函数。
通过以上简单的例子,我们得出了以下结论:defer的执行是按照先进后出的顺序被调用的,也就是栈模型