# Go语言中defer的执行顺序是怎样的?
以下为知识星球 (opens new window)录友分享的虾皮二面问题:”Go 里 defer 语句的执行顺序是怎样的?“(下文知识图解部分提供了执行顺序图示)
# 简要回答
defer 执行顺序和调用顺序相反,类似于栈后进先出(LIFO)。
当 defer 语句被执行时,跟在 defer 后面的函数会被延迟执行。直到包含该 defer 语句的函数执行完毕时,defer 后的函数才会被执行。
也可以在一个函数中执行多条 defer 语句,它们的执行顺序与声明顺序相反。
# 详细回答
在 Go 语言中,defer 语句的执行顺序严格遵循“后进先出”(LIFO)的栈式原则,即在当前函数中最后声明的 defer 会最先被执行。
从底层执行流程来看,defer 并非在 return 之后执行,而是插入在“返回值赋值”与“RET 指令”之间:
当函数执行到返回点时,Go 运行时会首先将结果写入返回值内存地址,接着按逆序调用所有注册的 defer 函数,最后才执行 RET 指令真正退出函数。
# 知识图解
defer语句的执行顺序:

# 知识扩展
面试官可能会追问:
Q1:defer 的作用或者使用场景是什么?
A1:Go 语言中的 defer 关键字用于延迟执行一个函数或方法调用。其核心作用是确保某些操作在包含它的函数执行完毕、即将返回之前(无论函数是正常返回还是因异常 panic 中断)被执行。
它最主要的使用场景是资源管理,例如在打开文件或获取锁之后,立即使用 defer 来安排关闭文件或释放锁,从而有效避免资源泄漏。此外,defer 也常与 recover 结合用于异常恢复,在 panic 发生时捕获异常,防止程序崩溃,并为记录错误或执行清理提供机会。
Q2:defer 能修改函数的返回值吗?
A2:可以,但前提是函数使用了具名返回值。
Go 语言中的 return 不是原子操作,它分为“给返回值赋值”和“执行 RET 指令”两步。defer 语句恰好插入在这两步之间执行。
如果返回值有名字,defer 可以在赋值之后、RET 之前修改这个变量,从而改变最终返回给调用者的结果。
反之,如果是匿名返回值,Go 会创建一个临时变量存储结果,defer 无法访问这个临时变量,因此无法修改最终结果。
Q3:在 for 循环里使用 defer 会有什么问题?怎么解决?
A3:在 for 循环中直接使用 defer 是极度危险的。
因为 defer 是绑定在当前函数作用域上的,而不是循环块作用域。
这意味着循环每运行一次,就会压入一个 defer 调用到栈中,直到整个外部函数结束时才会统一执行。
在大规模循环中,这不仅会导致栈内存迅速膨胀,更严重的是会导致资源无法及时释放,引发资源耗尽。
正确的做法是将循环体封装在一个匿名函数中并立即执行,利用独立的作用域来确保每次循环结束时立即触发 defer 释放资源。
评论
验证登录状态...