协程内存不降的主因是__closure__意外持有大对象,需用inspect.getclosurevars(coro)确认捕获变量,改闭包引用为显式参数传递,并避免task被全局强引用。

__closure__ 持有大对象是协程内存不降的最常见原因,不是 GC 失效,也不是你忘了 await —— 是协程对象活着,它闭包里绑着的 config_dict、model_weights 或整个 self 就没法释放。
怎么确认闭包真在拖内存?
别猜,直接看:inspect.getclosurevars(coro) 能列出协程实际捕获了哪些变量;配合 coro.cr_frame.f_locals(调试时)可验证是否意外带入了不该有的大数据结构。
常见错误现象:psutil.Process().memory_info().rss 持续上涨,但 gc.collect() 后几乎不变;objgraph.show_growth() 显示大量 coroutine 实例堆积,且每个都比预期大得多。
- 如果
getclosurevars(coro).nonlocals里出现big_cache、request_body、db_pool这类变量,基本就是根因 - 注意:类方法中定义的协程会默认闭包捕获
self,哪怕你只用了self.client -
async def函数体外定义的大对象(比如模块级GLOBAL_CONFIG),只要被协程函数引用,就会进__closure__
把闭包捕获改成参数传入
这是最干净、最可控的解法。闭包不是不能用,而是不能“偷偷”捕获重量级对象。
错误写法:cache = big_cache; async def fetch(url): return await http.get(url, cache=cache) → cache 被闭包持有
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
正确写法:async def fetch(url, cache): return await http.get(url, cache=cache),调用时显式传参
- FastAPI 路由里尤其要注意:别在协程里直接引用
request.state.user_data,改用async def handler(request): data = request.state.user_data; return await do_work(data) - 类方法中,提前解包:
client = self.client; session = self.session,再把client和session当参数传给内部协程 - 如果必须复用逻辑,用
functools.partial绑定参数,而不是靠闭包——partial(fetch, cache=small_cache)不会拖累大对象
避免协程对象被隐式强引用
即使你改成了参数传入,如果协程对象本身被存进全局列表、字典或日志 extra,照样卡住内存。
典型陷阱:logger.info("task started", extra={"task": asyncio.create_task(coro)}) → extra 字典强引用了 Task,而 Task 又持有了协程和它的闭包
- 用
weakref.WeakSet()替代普通集合跟踪任务:_live_tasks = weakref.WeakSet(); _live_tasks.add(asyncio.create_task(coro)) - 所有
asyncio.create_task()必须配生命周期管理:要么await它,要么明确cancel()+await task,不能“发完就不管” - 检查第三方库是否缓存了
Task或协程对象(如旧版aiofiles、某些异步 ORM),升级前查 changelog
协程挂起时间长时更要小心
一个 await asyncio.sleep(300) 的协程,只要没执行完,它持有的闭包变量就一直驻留。这在定时任务、长轮询、慢接口等待场景下极易暴露问题。
- 对可能长时间挂起的协程,强制拆分:把“准备阶段”(加载大配置)和“等待阶段”(sleep / I/O)分开,前者同步做完,后者只传必要参数
- 不要在
async with块外持有资源句柄(如文件、连接),否则异常未被捕获时__aexit__不执行,底层资源锁死 -
tracemalloc快照要在协程刚创建时就打,等内存涨起来再拍已经晚了;gc.get_referrers(task)要重点盯frame和extra字典
async def 不是魔法,是对象生成器。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










