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