panic沿调用栈逐层回退,每退出一层即执行该层defer(LIFO顺序),直至遇到recover或到达栈顶;recover仅在defer函数内调用才有效,且只捕获当前goroutine未被处理的panic。

panic在函数调用栈中如何向上传播
panic不是“跳转”,而是沿着当前goroutine的调用栈逐层回退,每退出一层函数,就执行该层已注册的defer语句,直到遇到recover或栈顶无更多函数可退。传播过程不可跳过中间层,也不受函数是否导出、是否内联影响。
为什么recover必须写在defer里才能生效
recover只在defer函数体内调用才有效,且仅能捕获**当前goroutine**中尚未被处理的panic。如果把它写在普通代码块或非defer函数中,返回值永远是nil。
- panic发生时,控制权不会立即跳到recover所在行,而是先完成当前函数所有已注册的defer(按LIFO顺序)
- 只有当某个defer函数内部调用
recover(),且此时panic尚未被其他recover捕获,才会成功截获并返回panic值 - 一旦recover成功,panic终止传播,后续defer仍会执行,但不会再触发崩溃
多层嵌套中recover只能捕获“最近未被捕获”的panic
如果A调用B,B调用C,C中panic,B中defer+recover,那么A完全感知不到这次panic——它被B拦截了。但如果B的recover之后又主动panic("again"),这个新panic会继续向上传给A。
- 每个goroutine维护独立的panic状态;recover清空的是当前goroutine的panic标志
- 嵌套调用中,后发生的panic会覆盖前一个未被捕获的panic(不是叠加,是替换)
- 常见错误:在多个defer里都写recover,但只有最内层那个能真正捕获——外层recover拿到的是
nil
嵌套调用里panic传递时的资源清理行为
panic触发后,Go保证所有已注册的defer都会执行,哪怕是在深层函数中panic,外层函数的defer也照常运行。这是资源安全的关键保障。
立即学习“go语言免费学习笔记(深入)”;
- 文件
Close()、锁Unlock()、HTTP响应Body.Close()等必须靠defer注册,不能依赖return路径 - 注意defer闭包捕获变量的时机:defer语句注册时捕获的是变量的当前地址或值,不是执行时的快照
- 若在循环中注册多个defer,且都引用同一个变量(如
i),最后所有defer看到的可能是同一个终值


















