Go递归无层数上限,仅受单goroutine栈空间限制(默认约1GB),超限直接panic且recover无效;必须显式传入depth参数并校验,防止恶意嵌套导致崩溃。

Go 里递归调用栈没有“层数上限”这个概念,只有单个 goroutine 的栈空间上限(默认约 1GB),一旦压栈总大小超限,就会直接 panic: runtime: goroutine stack exceeds 1GB limit —— 这不是你能 catch 住的错误,也不是语法问题,是运行时强制终止。
为什么 recover() 对栈溢出完全无效
栈溢出发生在 Go 运行时(runtime)层面,早于 Go 的 panic 机制。defer + recover() 只能捕获 Go 层面抛出的 panic,而栈超限是 runtime 直接 kill goroutine,根本不会走 panic 流程。
- 实测:在递归入口加
defer func() { recover() }(),照样 panic,控制台连 recover 日志都不会打印 - 别把
recover()当安全网,它对stack exceeds类错误 0% 生效 - 真正要防的不是“会不会崩”,而是“崩之前能不能提前拒绝”
怎么写递归函数才不至于线上突然挂掉
所有可能被用户输入或嵌套结构触发的递归(比如解析 JSON、模板 include、路径展开),必须显式传入 depth 参数,并在第一行校验。
- 初始调用传
0,每次递归传depth + 1 - 校验写法:
if depth > maxDepth { return errors.New("recursion too deep") } -
maxDepth不能硬编码为100;HTTP 头嵌套通常 ≤ 5 层,YAML include ≤ 8 次,这些得写进常量注释或配置里 - 终止条件必须覆盖所有输入分支:比如
node == nil,而不是node.Left == nil;阶乘函数要判n ,不能只判 <code>n == 0
用 []*Node 模拟栈替代递归时最容易漏的三件事
迭代不是简单把 dfs(node.Left) 换成 stack = append(stack, node.Left) 就完事。真实场景中,漏掉任意一点都会导致死循环、panic 或逻辑错。
立即学习“go语言免费学习笔记(深入)”;
- 入栈前必须判空:
if node != nil { stack = append(stack, node) },否则空指针进栈后 pop 出来继续处理,无限循环 - 图遍历必须配
visited map[*Node]bool,光靠len(stack) == 0判断结束会重复访问节点,尤其有环结构时直接卡死 - 预分配切片容量:
stack := make([]*Node, 0, 1024),避免频繁扩容带来的内存抖动和 GC 压力
最易被忽略的点:深度防护不是“改完迭代就高枕无忧”。即使用了显式栈,也要在 append 前检查 len(stack) > maxDepth —— 因为恶意输入可能构造 5000 层嵌套,你模拟的栈照样会吃光内存,只是换了个崩溃方式。


















