Go语言【学习】defer和逃逸分析
2024-09-04 13:38:19
defer
什么是defer?
defer是Go语言的一中用于注册延迟调用的机制,使得函数活语句可以再当前函数执行完毕后执行
为什么需要defer?
Go语言提供的语法糖,减少资源泄漏的发生
如何使用defer?
在创建资源语句的附近,使用defer语句释放资源
示例一:
/*func f1()(r int){
t := 5
// 1.赋值指令
r = t
// defer在赋值和返回之间执行
defer func() {
t = t + 5
}()
// 空的return指令
return t
}*/ package main import "fmt" func f1()(r int){
t := 5
defer func() {
t = t + 5
}()
return t
} func main() {
fmt.Println(f1())
} //5
执行return指令时,首先会把返回值copy到栈上,返回空的IP指令;return时先执行了赋值操作r=t,后执行defer操作,最后r的值没有修改
示例二:
package main import "fmt" func f2()(r int){
defer func() {
r = r + 5
}()
return 1
} func main() {
fmt.Println(f2())
} //6
能懂示例一,这个自然懂
示例三:
package main import "fmt" func f3()(r int){
defer func(r *int) {
*r = *r + 5
}(&r)
return 1
} func main() {
fmt.Println(f3())
} //6
defer传入的是指针,修改会改变原来的值,所以依然是6
示例四:
package main import (
"errors"
"fmt"
) func e1(){
var err error
// 压栈的时候 err已经变成nil值
defer fmt.Println("e1",err)
err = errors.New("defer1 error")
fmt.Println(err)
return
} func e2(){
var err error
// 闭包err是外部err的引用
defer func() {
fmt.Println("e2",err)
}()
err = errors.New("defer2 error")
return
} func e3(){
var err error
// 参数拷贝时就是nil
defer func(err error) {
fmt.Println("e3",err)
}(err)
err = errors.New("defer3 error")
return
} func main() {
e1()
e2()
e3()
} //e1 <nil>
//e2 defer2 error
//e3 <nil>
示例五:
package main import "fmt" func main() {
var a = accumulator()
fmt.Println(a(1))
fmt.Println(a(10))
fmt.Println(a(100))
var b = accumulator()
fmt.Println(b(1))
fmt.Println(b(10))
fmt.Println(b(100))
} func accumulator() func(int) int{
var x int return func(i int) int {
fmt.Printf("(%+v, %+v) - ",&x,x)
x += i
return x
} } //(0xc00000a0b8, 0) - 1
//(0xc00000a0b8, 1) - 11
//(0xc00000a0b8, 11) - 111
//(0xc00000a120, 0) - 1
//(0xc00000a120, 1) - 11
//(0xc00000a120, 11) - 111
示例六(执行顺序):
package main import (
"fmt"
"time"
) func main() {
defer fmt.Println("defer main")
var user = "" go func() {
defer func() {
fmt.Println("defer caller")
if err := recover(); err != nil{
fmt.Println("recover success . err:", err)
}
}() func(){
defer func() {
fmt.Println("defer here")
}()
if user == ""{
panic("should set user env.")
}
}()
}()
time.Sleep(time.Second)
fmt.Println("end") }
示例七:
package main import "fmt" func main() { for i := 0; i < 5; i++ {
defer fmt.Println(i,1) } for i := 0; i < 5; i++ {
defer func(){
fmt.Println(i,2)
}()
} for i := 0; i < 5; i++ {
defer func(){
j := i
fmt.Println(j,3)
}()
} for i := 0; i < 5; i++ {
j := i
defer fmt.Println(j,4)
} for i := 0; i < 5; i++ {
j := i
defer func() {
fmt.Println(j,5)
}()
} // 拷贝传值
for i := 0; i < 5; i++ {
defer func(j int) {
fmt.Println(j, 6)
}(i)
}
} //4 6
//3 6
//2 6
//1 6
//0 6
//4 5
//3 5
//2 5
//1 5
//0 5
//4 4
//3 4
//2 4
//1 4
//0 4
//5 3
//5 3
//5 3
//5 3
//5 3
//5 2
//5 2
//5 2
//5 2
//5 2
//4 1
//3 1
//2 1
//1 1
//0 1
//
逃逸分析
什么是逃逸分析?
Go语言编译器执行静态代码分析后,决定哪些变量逃逸到堆上
为什么需要逃逸分析?
尽可能将变量分配到栈上
逃逸分析如何进行?
只有在编译器能证明变量在函数返回后不再被引用的,才会分配到栈上,其他情况分配到堆上
总结:
动态内存分配(堆上)比静态内存分配(栈上)开销要大的多
如果变量在函数外部没有引用,则优先放到栈中;如果在函数外部存在引用,则必定放到堆中;
示例一:
package main type S1 struct {} func main() {
var x S1
_ = indentity1(x)
} func indentity1(x S1) S1{
return x
}
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape1.go Z:\src\defer和逃逸分析>
没有逃逸,值传递,直接在栈上分配。Go语言函数传递都是通过值的,调用函数的时候,直接在栈上copy出一份参数,不存在逃逸
示例二:
package main type S2 struct {} func main() {
var x S2
y := &x
_ = indentity2(y)
} func indentity2(x *S2) *S2{
return x
}
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape2.go
# command-line-arguments
.\escape2.go:11:17: leaking param: x to result ~r1 level=0
.\escape2.go:7:7: main &x does not escape Z:\src\defer和逃逸分析>
x未发生逃逸,identity函数的输入直接当成返回值了,没有对x进行引用,所以x没有逃逸。
示例三:
package main type S3 struct {} func main() {
var x S3
_ = *ref3(x)
} func ref3(z S3) *S3{
return &z
}
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape3.go
# command-line-arguments
.\escape3.go:11:9: &z escapes to heap
.\escape3.go:10:11: moved to heap: z Z:\src\defer和逃逸分析>
示例四:
package main type S4 struct {
M *int
} func main() {
var i int
_ = ref4(i)
} func ref4(y int) (z S4){
z.M = &y
return z
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape4.go
# command-line-arguments
.\escape4.go:13:8: &y escapes to heap
.\escape4.go:12:11: moved to heap: y Z:\src\defer和逃逸分析>
示例五:
package main type S5 struct {
M *int
} func main() {
var i int
ref5(&i)
} func ref5(y *int) (z S5){
z.M = y
return z
}A
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape5.go
# command-line-arguments
.\escape5.go:12:11: leaking param: y to result z level=0
.\escape5.go:9:7: main &i does not escape Z:\src\defer和逃逸分析>
示例六:
package main type S6 struct {
M *int
} func main() {
var x S6
var i int
ref6(&i,&x)
} func ref6(y *int, z *S6){
z.M = y
}
逃逸检查
Z:\src\defer和逃逸分析>go build -gcflags "-m -l" escape6.go
# command-line-arguments
.\escape6.go:13:11: leaking param: y
.\escape6.go:13:19: ref6 z does not escape
.\escape6.go:10:7: &i escapes to heap
.\escape6.go:9:6: moved to heap: i
.\escape6.go:10:10: main &x does not escape
最新文章
- 简单好记的Jdk 环境变量配置
- 理解和使用 JavaScript 中的回调函数
- SQL 数据库 触发器 、事务
- Windows命令行使用FTP
- [Google Code Jam (Round 1A 2008) ] A. Minimum Scalar Product
- C与C++不同
- C#中“走马灯”和类似“打地鼠”的小程序(Seventeenth Day)
- hibernate_@GeneratedValue
- SDAU课程练习--problemA(1000)
- swift中的AnyHashable
- vue.js中的各种问题记录(包括环境问题和学习笔记)
- SpringMVC 知识整理
- 张金禹 C语言--第0次作业
- version.go
- 使用EF+ASP.NET MVC+Bootstrap开发一个功能强大的问卷调查系统
- xampp+YII搭建网站
- php+C#.net混合开发
- Android 建立手机与手表数据同步机制总结
- Android播放器推荐:可以播放本地音乐、视频、在线播放音乐、视频、网络收音机等
- TOP100summit:【分享实录-封宇】58到家多端消息整合之路