标准答案

  1. 统计 asyncio.all_tasks、线程数、子进程、连接池和队列年龄,观察流量回落后是否恢复。
  2. 检查未 await 的 coroutine、失去引用的 Task、永久 Future、线程阻塞和进程未 join。
  3. 为任务设置 deadline、取消和 finally 清理,关闭 executor、client 和临时文件。
  4. 用 dump、tracemalloc、py-spy 和资源指标验证泄漏是否在压力回落后停止。

题目解析

泄漏排查先看对象是否仍然被引用,再看外部资源是否仍然活着。asyncio Task 可能因为回调、队列或全局集合长期持有;线程可能卡在不可中断的 I/O;子进程可能没有 wait;连接、文件和临时目录则可能在 Python 对象被回收前一直占用系统资源。

流量回落是很有价值的验证条件:真正的短期缓存或工作集通常会趋于稳定,泄漏会继续增长。可以按时间采集任务数、线程数、子进程、打开文件、连接池借出数、队列年龄和 RSS,再用任务栈、线程 dump、tracemalloc 或系统工具定位创建点。

修复的重点是资源所有权和生命周期:每个 Task 有取消和异常处理,每个 executor 有 shutdown deadline,每个外部客户端和文件有上下文管理。gc.collect 只能回收满足条件的 Python 对象,不能解决阻塞、未 join 或未 close。

常见误区

  • 误区:内存上涨就只调用 gc.collect。改正:区分 Python 对象、native 内存和外部资源,先确认引用链与资源计数。
  • 误区:创建 Task 后不保存引用也不读取异常。改正:集中管理 Task,读取结果或异常,并在 shutdown 时取消和等待。
  • 误区:线程池 shutdown 没有等待期限。改正:设置取消或停止信号、等待 deadline,并为无法中断的任务保留强制退出方案。

作者信息