golang反编译_【Golang】脱胎换骨的defer(一)

本文探讨了Go语言中defer的使用,包括其如何延迟执行、为何倒序执行的原理。通过分析deferproc和deferreturn的伪代码,揭示了defer在函数返回前执行以及倒序执行的原因。同时,提到了每个goroutine的_defer字段作为defer链表,以及在注册和执行defer时涉及的参数和状态信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

83991c6530f6ed1992a8c3f673e58a44.png

Go语言的defer是一个很方便的机制,能够把某些函数调用推迟到当前函数返回前才实际执行。我们可以很方便的用defer关闭一个打开的文件、释放一个Redis连接,或者解锁一个Mutex。而且Go语言在设计上保证,即使发生panic,所有的defer调用也能够被执行。不过多个defer函数是按照定义顺序倒序执行的。 我们在公众号有一篇文章:

【Golang】脱胎换骨的defer​mp.weixin.qq.com
32f5e7eaaa60bc8bb1d29573de064d12.png

内容有点儿多,篇幅有点儿长,所以在这里我们打算拆分成四篇文章,每一篇专注一两个主要问题,可能会好消化些吧~

(一) defer如何延迟,因何倒序?

(二) defer函数怎样传参?

(三) defer+闭包,再多套几层,你还hold住吗?

(四) 都说defer1.12性能有坑,那坑从何来?又该怎么填?

c8e1b007a131cd8c1f500459698d5e93.png
func f1() {
    defer A()
    // code to do something
}

像这样一段代码,在Go1.12中编译后的伪指令是这样的(源码结合反编译整理出的伪代码,帮助理解~_~):

func f1() {
    r := runtime.deferproc(0, A) // 经过recover返回时r为1,否则为0
    if r > 0 {
        goto ret
    }
    // code to do something
    runtime.deferreturn()
    return
ret:
    runtime.deferreturn()
}

其中与defer指令相关的有两个部分。第一部分是deferproc,它负责保存要执行的函数信息,我们称之为defer“注册”

func deferproc(siz int32, fn *funcval)

从函数原型来看,deferproc函数有两个参数,第一个是被注册的defer函数的参数加返回值共占多少字节;第二个参数是一个runtime.funcval结构体的指针,也就是一个Function Value。对Function Value感兴趣,可以看看这个:

网页链接​mp.weixin.qq.com
914486906b8feb40d2bae04e7bc72c55.png

与defer指令相关的第二部分就是deferreturn,它被编译器插入到函数返回以前调用,负责执行已经注册的defer函数。所以defer函数之所以能延迟到函数返回前执行,就是因为先注册,后调用。

201ef728c193be0c98a661a7abf59901.png

再来看看defer函数为什么会倒序执行。defer注册信息会保存到defer链表。每个goroutine在运行时都对应一个runtime.g结构体,其中有一个_defer字段,保存的就是defer链表的头指针。

aef8f758d6b132d803115a1dde343b68.png

deferproc新注册的defr信息会添加到链表头。deferreturn执行时也从链表头开始,所以defer才会表现为倒序执行。

355fba33e1cb4848ef306136be9d7a47.png

理解了这些,就可以继续细化,看看defer注册时保存了什么信息,defer链表中每个元素究竟是什么结构了。

731f7459df882e789310f93b2f96c61b.png
type _defer struct {
    siz       int32
    started   bool
    sp        uintptr // sp at time of defer
    pc        uintptr
    fn        *funcval
    _panic    *_panic // panic that is running defer
    link      *_defer
 }

siz:由deferproc第一个参数传入,就是defer函数参数加返回值的总大小。这段空间会直接分配在_defer结构体后面,用于在注册时保存给defer函数传入的参数,并在执行时直接拷贝到defer函数的调用者栈上。

started :标识defer函数是否已经开始执行;

sp:就是注册defer函数的函数栈指针;

pc:是deferproc函数返回后要继续执行的指令地址;

fn:由deferproc的第二个参数传入,也就是被注册的defer函数;

_panic:是触发defer函数执行的panic指针,正常流程执行defer时它就是nil;

link:自然是链到之前注册的那个_defer结构体。

网页链接​mp.weixin.qq.com
3d679dc32f52521a73196e590f027cce.png

3e4765da4dee3fce6425d84bcaf2decb.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值