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

  

Go语言【学习】defer和逃逸分析的更多相关文章

  1. R语言学习笔记-Corrplot相关性分析

    示例图像 首先安装需要的包 install.packages("Corrplot") #安装Corrplot install.packages("RColorBrewer ...

  2. 20155229-付钰涵-分析自我技能延展到c语言学习状况

    我的小技能 我记得幼儿园时表演的舞蹈,也记得从水彩到素描的学习,还记得小学和初中获得的钢琴省级奖项. 舞蹈止于一年级,绘画止于三年级,钢琴从学前班到高一那十年的时间里有过断续. 03年-04年的那个冬 ...

  3. 足球运动训练心得及经验分析-c语言学习调查

    在准备预备作业02之前,我参考娄老师的提示,阅读了<[做中学(Learning By Doing)]之乒乓球刻意训练一年总结>一文. 在文章描述的字里行间,给予我的印象是系统.负责,娄老师 ...

  4. Go 语言机制之逃逸分析

    https://blog.csdn.net/weixin_38975685/article/details/79788254   Go 语言机制之逃逸分析 https://blog.csdn.net/ ...

  5. 基于Golang的逃逸分析(Language Mechanics On Escape Analysis)

    何为逃逸分析 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针.它涉及到指针分析和形状分析. 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针 ...

  6. 小师妹学JVM之:逃逸分析和TLAB

    目录 简介 逃逸分析和栈上分配 TLAB简介 TLAB详解 设置TLAB空间的大小 TLAB中大对象的分配 TLAB空间中的浪费 总结 简介 逃逸分析我们在JDK14中JVM的性能优化一文中已经讲过了 ...

  7. 技能收获与C语言学习

    你有什么技能比大多人(超过90%以上)更好? 我会的东西很多,喜欢的东西太多,但是很遗憾广而不专,会而不精.学了很多东西我都是为了娱乐,因为以前我们那里过于强调学习,很多爱好也都被扼杀在摇篮里.我觉得 ...

  8. go语言学习笔记

    Go语言学习基本类型Bool 取值范围:true,false (不可以用数字代替)Int/uint 根据平台可能为32或64位int8/uint8 长度:1字节 取值范围-128~127/0~255b ...

  9. 20155306白皎 学习技能+C语言学习

    你有什么技能比大多数人更好 谈起技能,我还有感觉有微微拿得出手的也只有主持这一项才艺了吧.从小学到高中一直参加朗诵比赛,以及从小学到大学一直在所在学校有担任过主持工作. 上大学以来,也参加了院级朗诵比 ...

随机推荐

  1. match 和 search 和 indexOf 查找及 正则表达式的 exec 和 test 用法

    function test(){ var name= "1.087"; var abc = "abd wor66k ne78xt"; var reg = /\d ...

  2. C++中vector的使用总结

    vector简单说明 vector也是一个容器,并且是个顺序容器.顺序容器有可变长数组vector.双向链表list.双端队列deque. 顺序容器的定义,是因为容器元素的位置和他们的值大小无关,也就 ...

  3. windows内核代码之进程操作

    [toc] 一丶简介 整理一下windows内核中.常用的代码.这里只整理下进程的相关代码. 二丶 windows内核之遍历进程 内核中记录进程的结构体是EPROCESS结构.所以只需要遍历这个结构即 ...

  4. nginx 反向代理之 proxy_set_header

    proxy_set_header用来设定被代理服务器接收到的header信息. 语法:proxy_set_header field value; field :为要更改的项目,也可以理解为变量的名字, ...

  5. .NET项目发布到本地IIS完整流程(VS2015)

    概要: 一.安装IIS功能 二.建立发布网站 三.发布应用程序 四.发布后各种问题的解决. [可先看概要四,可避免很多坑] 具体操作: 一.安装IIS功能 选择必要的功能进行安装,重启有效. 二.建立 ...

  6. saltstack自动化运维工具搭建个人笔记

    至于为什么选择saltstack,因为Puppet.Chef基于Ruby开发,而ansible.saltstack基于python开发,便于后期二次,良好的可移植性. 又,ansible基于SSH协议 ...

  7. Spring Boot打war包和jar包的目录结构简单讲解

    Spring Boot项目可以制作成jar包和war包,其目录结构是不一样的,具体的如下所示: 1.war包目录结构分析WAR(Web Archivefile)网络应用程序文件,是与平台无关的文件格式 ...

  8. Tocmat 统计tomcat进程内的线程数

    获取tomcat进程pid ps -ef | grep tomcat 统计该tomcat进程内的线程个数 ps -Lf  558899 | wc -l

  9. grpc使用记录(三)简单异步服务实例

    目录 grpc使用记录(三)简单异步服务实例 1.编写proto文件,定义服务 2.编译proto文件,生成代码 3.编写服务端代码 async_service.cpp async_service2. ...

  10. docker build提示error checking context:can't stat xxx

    现象描述 使用docker build一个镜像的时候,提示下面的错误: ➜ docker build -t image_name -f xxx.dockerfile . error checking ...