bigywater.tech
常见报错 · 56

Python RecursionError: maximum recursion depth exceeded:默认第 999 层失败,setrecursionlimit 设到一千万纯 Python 递归 500 万层照常跑(约 607 MiB),json/repr 的深递归仍报 Stack overflow (used 16352 kB)

“RecursionError” 的常见建议是 sys.setrecursionlimit,同时有人警告调大会让解释器崩溃。这篇用 Python 3.14.9 和 3.15.0 在本机验证:各种触发方式的报错原文、调大上限实际会怎样(内存还是崩溃)、C 代码里的深递归是另一道保护、线程栈有没有影响,以及哪些“修法”其实无效。

999
默认上限 1000 时,漏终止条件的递归实际失败的层数
500 万层
上限设 1000 万时纯 Python 递归成功的层数,峰值内存约 607 MiB(3.14.9),没有崩溃
16352 kB
json、repr、比较这类经过 C 代码的深递归报的 “Stack overflow (used … kB)” 的数字,与 setrecursionlimit 无关

默认上限

实测 · 本机执行输出
  Python 3.14(退出码 0):
      sys.getrecursionlimit() = 1000
      到第 999 层时失败: RecursionError: maximum recursion depth exceeded
  Python 3.15(退出码 0):
      sys.getrecursionlimit() = 1000
      到第 999 层时失败: RecursionError: maximum recursion depth exceeded
  不捕获时的完整报错尾部(含被折叠的重复栈帧):
  Python 3.15(退出码 1):
      Traceback (most recent call last):
        File "<tmp>/s3.py", line 2, in <module>
          f()
          ~^^
        File "<tmp>/s3.py", line 1, in f
          def f(): f()
                   ~^^
        File "<tmp>/s3.py", line 1, in f
          def f(): f()
      …(共 15 行)

1000,实际在第 999 层失败。

报错的变体

实测 · 本机执行输出
  普通函数无限递归:
      3.14: rc=1 RecursionError: maximum recursion depth exceeded
      3.15: rc=1 RecursionError: maximum recursion depth exceeded
  属性自己调用自己(@property 里读了自己):
      3.14: rc=1 RecursionError: maximum recursion depth exceeded
      3.15: rc=1 RecursionError: maximum recursion depth exceeded
  __getattr__ 里读了没定义的属性:
      3.14: rc=1 RecursionError: maximum recursion depth exceeded
      3.15: rc=1 RecursionError: maximum recursion depth exceeded
  __repr__ 里又 repr 自己:
      3.14: rc=1 RecursionError: maximum recursion depth exceeded
      3.15: rc=1 RecursionError: maximum recursion depth exceeded
  json.loads 深嵌套数组:
      3.14: rc=1 RecursionError: Stack overflow (used 16352 kB) while decoding a JSON array from a unicode string
      3.15: rc=1 RecursionError: Stack overflow (used 16352 kB) while decoding a JSON array from a unicode string
  深嵌套列表 repr:
      3.14: rc=1 RecursionError: Stack overflow (used 16352 kB) while getting the repr of an object
      3.15: rc=1 RecursionError: Stack overflow (used 16352 kB) while getting the repr of an object
  copy.deepcopy 深嵌套:
      3.14: rc=1 RecursionError: maximum recursion depth exceeded
      3.15: rc=1 RecursionError: maximum recursion depth exceeded
  比较两个深嵌套列表 ==:
      3.14: rc=1 RecursionError: Stack overflow (used 16352 kB) in comparison
      3.15: rc=1 RecursionError: Stack overflow (used 16352 kB) in comparison

纯 Python 递归是 maximum recursion depth exceeded;经过 C 代码的是 Stack overflow (used … kB)。

调高上限

实测 · 本机执行输出
  Python 3.14,纯 Python 递归 100000 层(上限设 1000 万):退出码 0;纯 Python 递归 100000 层成功,结果 100000 | 峰值内存(ru_maxrss)约 28 MiB
  Python 3.14,纯 Python 递归 1000000 层(上限设 1000 万):退出码 0;纯 Python 递归 1000000 层成功,结果 1000000 | 峰值内存(ru_maxrss)约 166 MiB
  Python 3.14,纯 Python 递归 5000000 层(上限设 1000 万):退出码 0;纯 Python 递归 5000000 层成功,结果 5000000 | 峰值内存(ru_maxrss)约 607 MiB
  Python 3.15,纯 Python 递归 100000 层(上限设 1000 万):退出码 0;纯 Python 递归 100000 层成功,结果 100000 | 峰值内存(ru_maxrss)约 28 MiB
  Python 3.15,纯 Python 递归 1000000 层(上限设 1000 万):退出码 0;纯 Python 递归 1000000 层成功,结果 1000000 | 峰值内存(ru_maxrss)约 166 MiB
  Python 3.15,纯 Python 递归 5000000 层(上限设 1000 万):退出码 0;纯 Python 递归 5000000 层成功,结果 5000000 | 峰值内存(ru_maxrss)约 726 MiB
  Python 3.14,repr 嵌套 10000 层的列表(上限设 1000 万):退出码 0;repr 成功,长度 20002
  Python 3.14,repr 嵌套 200000 层的列表(上限设 1000 万):退出码 0;RecursionError: Stack overflow (used 16352 kB) while getting the repr of an object
  Python 3.15,repr 嵌套 10000 层的列表(上限设 1000 万):退出码 0;repr 成功,长度 20002
  Python 3.15,repr 嵌套 200000 层的列表(上限设 1000 万):退出码 0;RecursionError: Stack overflow (used 16352 kB) while getting the repr of an object

纯 Python 递归的代价是内存而不是崩溃;C 代码里的递归仍有独立的栈保护。

线程里

实测 · 本机执行输出
  Python 3.14,threading.stack_size=默认:退出码 0;线程栈 默认 -> [50000]
  Python 3.14,threading.stack_size=262144:退出码 0;线程栈 262144 -> [50000]
  Python 3.14,threading.stack_size=67108864:退出码 0;线程栈 67108864 -> [50000]
  Python 3.15,threading.stack_size=默认:退出码 0;线程栈 默认 -> [50000]
  Python 3.15,threading.stack_size=262144:退出码 0;线程栈 262144 -> [50000]
  Python 3.15,threading.stack_size=67108864:退出码 0;线程栈 67108864 -> [50000]

纯 Python 递归不受线程栈大小影响。

修法

实测 · 本机执行输出
  Python 3.14:递归 -> RecursionError ; 迭代 -> 100000 ; 写成尾递归 -> RecursionError ; 递归 + lru_cache -> RecursionError
  Python 3.15:递归 -> RecursionError ; 迭代 -> 100000 ; 写成尾递归 -> RecursionError ; 递归 + lru_cache -> RecursionError

改成迭代;尾递归写法和 lru_cache 都无效。

分步演示

递归深度
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Homebrew 的 Python 3.14.9 与 3.15.0(两个解释器并排跑同一份代码,都不是自由线程构建);每个例子是临时目录里的独立小脚本,用子进程运行并取真实的退出码与输出(路径换成 );内存用 resource.getrusage 的 ru_maxrss;数字每次运行会有小幅抖动
    • 没有验证:Linux 上的结果(栈大小、ulimit -s 不同,16352 kB 这类数字会变,C 栈保护的触发深度也会不同);自由线程构建和 JIT;Python 3.13 及更早版本(那时纯 Python 递归会占 C 栈,调大上限可能真的段错误,这里没有对比);异步函数、生成器里的递归;PyPy;递归深度里受 sys.setrecursionlimit 之外限制的其它第三方库(如 pickle、ast.parse 的深嵌套表达式)
    京ICP备2026067502号-1京公网安备11010502063487号