Go 面试突击:defer、recover、panic 经典 20 问
panic 和 recover 是 Go 的最后防线。掌握三者的执行机制,是 Gopher 项目的"基本礼仪"。
一、defer 基础
Q1. defer 执行顺序?
LIFO(后入先出)。
Q2. defer 内修改返回值能影响外面吗?
若返回值具名,可以;否则不行。
Q3. defer 调用时机?
return 语句执行前。
Q4. defer 内修改循环变量?
fori:=0;i<3;i++{deferfunc(iint){fmt.Println(i)}(i)}// 输出 2 1 0参数预计算。
Q5. defer 在 panic 后执行吗?
执行。
Q6. defer 性能?
低频用 OK。1.14+ 大部分 defer 编译器会 inline,免 runtime 开销。
Q7. defer 用于资源释放常见吗?
文件、锁、连接、ctx cancel 等。
Q8. defer 用于 trace 计数器?
defer trace.Counter("foo").Inc()。
Q9. defer 让方法变慢多少?
几纳秒到几十纳秒级。
二、panic 基础
Q10. panic 与 exception 区别?
Go panic 是语言级机制,exception 是语言级异常。Go 用 panic/recover,Java 用 exception。
Q11. panic 后整个进程死吗?
无 recover 则 panic 沿栈冒泡到 main,整个进程死。
Q12. panic 的副作用?
defer 仍执行;栈展开;状态机被打破。
Q13. panic 后函数还能用吗?
调用方 recover 后,goroutine 继续。
Q14. recover 何时调用才有用?
在 defer 函数内直接调用。
Q15. recover 多次调用可以吗?
第一次后无 effect,但可多次写。
Q16. 在 goroutine 里 panic?
只能由那个 goroutine recover。
Q17. panic 子函数影响父吗?
影响,子函数 panic 上抛。
三、实战
Q18. 全局 recover 中间件?
funcRecovery(c*gin.Context){deferfunc(){ifr:=recover();r!=nil{log.Printf("panic: %v\n%s",r,debug.Stack())c.AbortWithStatusJSON(500,gin.H{"msg":"internal"})}}()c.Next()}Q19. 测试 panic 应该用什么?
assert.Panics(func(){f()})Q20. 让 panic 输出的栈不重复?
errors stacktrace vs runtime/debug.Stack:errors.New不带栈,需要pkg/errors包装或runtime.Caller。
四、避坑
- 不要用 panic 实现业务错误——error 才是正路
- 不要在 M 关闭前忘记 recover——崩进程
- recover 仅为兜底——测试覆盖到不要靠它
- panic 包装成 err 后丢上下文——带 stack
五、总结与展望
defer + panic + recover 三件套是 Go 容错核心。最佳实践:
- 业务层用 error
- goroutine 用 defer recover 兜底
- HTTP/gRPC 入口统一 Recovery 中间件
未来 Go 1.24 可能引入panic为 typed panic 值。
六、参考文献
- Go 官方 blog:Defer, Panic, and Recover
- cmd/compile walk.go