网络异常,图片无法展示
|
defer的作用
defer
在go
中的作用就是延迟执行,一般常用来关闭一些io
流等。简单来个例子:
package main import "fmt" func main() { fmt.Println("1") defer func() { fmt.Println("2") }() fmt.Println("3") }
上面的例子输出:
1 3 2
从上面得知defer
会在程序结束前被执行,但是如果有多个defer
呢?这时候go
是执行的呢?
多个defer执行流程
在go1.12
版本中,defer
对应的是一个结构体_defer
。每个go
的goroutine
上都有一个指向这个结构体的指针*_defer
。所有_defer
通过结构体中的link
组成一个链表。每添加一个_defer
都会在链表头部上插入,所以就导致了先入后出现象。**所有的_defer
**结构体都在堆内分配内存。
频繁的堆分配势必影响性能,所以Go语言会预分配不同规格的deferpool,执行时从空闲_defer中取一个出来用。没有空闲的或者没有大小合适的,再进行堆分配。用完以后,再放回空闲_defer池。这样可以避免频繁的堆分配与回收。
网络异常,图片无法展示
|
通过上面的流程,我们就可以理解多个defer
的输出流程了
package main import "fmt" func main() { fmt.Println("1") defer func() { fmt.Println("2") }() defer func() { fmt.Println("3") }() defer func() { fmt.Println("4") }() fmt.Println("5") }
输出:
1 5 4 3 2
defer的优化
上面说到每个defer
都是由一个个结构组成的,所以go
会对多个同样的没有捕获参数的defer
做出优化。具体看图解
网络异常,图片无法展示
|
defer的参数取值
在defer
注册的时候,编译器会在它自己的个参数后面,开辟一段空间,用于存放defer函数的返回值和参数。这一段空间会在注册defer时,直接拷贝到_defer结构体的后面。
网络异常,图片无法展示
|
所以就可以解释下面这段代码了:
package main import "fmt" func main() { i:=1 defer T1(i) i++ } func T1(i int) { fmt.Println(i) }
输出:
1