【defer】全新升级1.14以后的defer|Go主题月

简介: 在go1.14中,官方又对defer做了升级,据说这次升级把速度提升了一个量级。

网络异常,图片无法展示
|

1.14版本中的defer


go1.14中,官方又对defer做了升级,据说这次升级把速度提升了一个量级。

网络异常,图片无法展示
|

在编译期间,会直接把defer放到函数末尾去执行,省去了_defer结构体和链表的使用。官方把这种方法命名为:开放编码(Open Coded)

不过需要满足以下条件,否则并不会使用开放编码:

  • 函数的 defer 数量少于或者等于 8 个;
  • 函数的 defer 关键字不能在循环中执行;
  • 函数的 return 语句与 defer 语句的乘积小于或者等于 15 个;

延迟比特

为什么上面说defer的数量要小于等于8个呢?这是由于延迟比特的限制。延迟比特只有8个,默认值为0,每个对应一个defer,延迟比特的作用是判断defer语句到底要不要执行,例如:

package main
import "fmt"
func main() {
  i:=1
  if i==1{
    defer fmt.Println("defer")
  }
}

defer外面有一个if判断语句,当判断语句为true时,就会把对应的defer比特位设为1 。然后在函数末尾每个defer都会判断对应的比特位记录是否为1,如果为1就执行,否则就不执行。

网络异常,图片无法展示
|

使用时机

在当前版本中,defer一共有三种执行方式,那go到底是如何判断当前的defer是该用哪种方式呢?

代码生成阶段的 cmd/compile/internal/gc.state.stmt 会负责处理程序中的 defer,该函数会根据条件的不同,使用三种不同的机制处理该关键字:

func (s *state) stmt(n *Node) {
  ...
  switch n.Op {
  case ODEFER:
    if s.hasOpenDefers {
      s.openDeferRecord(n.Left) // 开放编码
    } else {
      d := callDefer // 堆分配
      if n.Esc == EscNever {
        d = callDeferStack // 栈分配
      }
      s.callResult(n.Left, d)
    }func (s *state) stmt(n *Node) {
  ...
  }
}

panic问题

虽然最新版本中的defer速度非常快,但是当程序发送panic时,在这之后的正常逻辑就都不会执行了,而是直接去执行defer链表。那些使用**开放地址(open coded)在函数内展开,因而没有被注册到链表的defer函数要通过栈扫描的方式来发现。所以1.14版本中就添加了几个字段用来辅助panic的栈扫描。

网络异常,图片无法展示
|

回顾

目录
相关文章
|
监控 Kubernetes Go
全链路追踪 & 性能监控,GO 应用可观测全面升级
当前,大多数面向 Golang 应用的监控能力主要是通过 SDK 方式接入,需要开放人员手动进行埋点,会存在一定问题。对此,可观测 Go Agent 应运而生。本文介绍的阿里云可观测 Go Agent 方案,能通过无侵入的方式实现应用监控能力。
109405 115
|
4月前
|
安全 Go
defer关键字:延迟调用机制-《Go语言实战指南》
Go 语言中的 `defer` 是用于延迟执行函数调用的关键字,广泛应用于资源释放、异常捕获和日志记录等场景。它在函数返回前执行,支持栈式后进先出(LIFO)顺序,参数求值时机为声明时而非执行时。常见用法包括文件关闭、锁解锁及结合 `recover` 处理 panic。尽管高效,频繁使用可能带来性能开销,需谨慎处理。总结而言,`defer` 是构建健壮代码的核心工具之一。
掌握 Go 语言的 defer 关键字
掌握 Go 语言的 defer 关键字
实验深度理解Go中try...catch...的panic、defer、recover用法
文章通过实验代码演示了Go语言中如何使用panic、defer和recover函数来模拟try...catch...的异常处理机制,并详细解释了每个函数的作用和在异常处理中的使用场景。
139 0
|
Go 开发者
Golang深入浅出之-Go语言 defer、panic、recover:异常处理机制
Go语言中的`defer`、`panic`和`recover`提供了一套独特的异常处理方式。`defer`用于延迟函数调用,在返回前执行,常用于资源释放。它遵循后进先出原则。`panic`触发运行时错误,中断函数执行,直到遇到`recover`或程序结束。`recover`在`defer`中捕获`panic`,恢复程序执行。注意避免滥用`defer`影响性能,不应对可处理错误随意使用`panic`,且`recover`不能跨goroutine捕获panic。理解并恰当使用这些机制能提高代码健壮性和稳定性。
369 2
|
Java Go 区块链
【Go语言专栏】Go语言中的延迟执行与defer语句
【4月更文挑战第30天】Go语言的延迟执行与defer语句用于资源释放和错误处理。defer通过关键字定义,函数返回时执行,顺序与定义相反。参数在定义时求值。应用包括资源释放、错误处理、成对操作和函数包装,是Go编程的关键特性。
129 0
|
7月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
7月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
1月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
1月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。