golang defer实现

golang defer实现

1.现象

  • defer 会在函数return前执行
  • 如果发生非系统级别panic,defer依然执行;在defer执行过程中,如果有recover,那就捕获panic,否则defer链调用结束,协程就GG
  • 如果发生系统级别异常(如panic during malloc)则直接退出进程不会执行任何defer函数
  • 多个defer倒序执行,类似栈
  • defer语句的参数,是立即评估,而不是等到执行时才评估

2.实现

  • defer是编辑器通过在函数开始、结束处插入特殊代码实现的,纯编译期行为
我们写的函数如下:
func A() {
    defer B()
    // A_logic_code
}
编译器给我们偷偷插入了2行(实际不止哈,简要示意)
func A() {
   r := deferproc(8, B) // 8是什么先不管,总之记住,这里叫注册defer
   // A_logic_code
   runtime.deferreturn() // 调用defer链
   return
}
通过这个示意伪代码,可以很清晰理解现象1、现象4

8 是函数B的参数和返回值空间大小之和,用于在A的栈上分配空间给defer
  • 再说现象3

    多个defer的时候,依次执行,每次都将一个defer结构体插入到defer链的链表头,所以执行的时候,就是倒序了

  • 再说非系统级别panic

    • 总原则:先触发非系统级别panic函数的执行,然后gopanic函数去调用defer函数,此时会一路传递panic信息,panic不会阻断defer执行
    • 如果一路在延迟函数中没有recover函数的调用,则会到达该携程的起点,该携程结束
    • 再说一个特例,panic1触发defer1函数,defer1函数有产生新的panic2,panic2又触发defer2函数,defer2函数又产生新的panic3

      Go的机制是如果当前defer函数是由之前的panic触发的并且当前defer函数会触发新的panic,则前一个panic停止执行,意思是本例,最终只能捕获到panic3,前两个被覆盖吞掉了

3.性能问题和优化

  • go1.12问题
    • defer在堆上分配,执行的时候,拷贝回栈
    • defer链表本就低效(执行过程中可能产生新的defer insert,执行时又delete)
  • go1.13减少堆栈拷贝

    • 把defer结构体定义在原函数内部,分配在栈上,然后注册的时候,指向栈内地址

      defer结构体增加一个字段heap标识是否为堆分配

    • 不适用于显示、隐式循环defer

    • 提升 约30%

  • go1.14优化

    • 函数内联展开:更狠,不再注册defer链,而是直接开始处插入defer函数所需的参数的变量定义,return插入显示defer函数的代码

      简单来说,就是直接把defer函数的逻辑代码拷贝,嵌套到原函数,再适当的补齐defer函数执行所需的形参

    • 不适用于显示、隐式循环defer

    • 提升一个数量级

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值