Python aiohttp内存泄漏排查与tracemalloc实战

桃夭
桃夭
发布时间:2026-10-13 | 3人浏览过
原创

本文记录了一次使用aiohttp进行异步开发时遇到的内存泄漏问题。通过复现高并发POST请求场景,发现multidict库特定版本存在内存未释放现象。文章详细演示了如何利用Python标准库tracemalloc定位内存增长源头,最终定位至web_response.py第56行及CIMultiDict类,并指出该问题已在multidict 4.5.2版本中修复。

近期工作中开始深入使用Python协程相关技术,涉及aiohttp、aiomysql、aioredis等模块。在实际应用过程中遇到诸多问题,本文重点整理一次内存泄漏问题的排查与解决过程。

在常规Python开发中,内存管理往往容易被忽视(当然这可能也受限于个人技术深度),相比之下C/C++开发者对此更为敏感。然而,一旦Python程序出现内存泄漏,处理起来将极为棘手。

最近的一次代码实践中便遭遇了此类问题,庆幸的是最终确认并非业务代码逻辑错误,而是所依赖的第三方库存在内存管理缺陷。下文将通过简化代码模拟该内存异常现象,并完整记录排查解决步骤,旨在为遇到同类问题的开发者提供参考。

一、问题复现

此次问题源于使用aiohttp构建接口服务。复现过程相对直观:我们构建一个接收POST请求的服务端,并配合一个高并发客户端进行访问,以此观察内存占用变化。

注意:该问题出现在特定库版本中:multidict==4.5.1,。在撰写本文两小时前,作者已发布4.5.2版本修复此内存问题,经测试验证该版本已无泄漏现象。

服务端代码实现如下:

from aiohttp import web
async def hello(request):
 return web.json_response(await request.json())
app = web.Application()
app.add_routes([web.post('/', hello)])
web.run_app(app)

客户端并发测试代码实现如下:

import asyncio
import aiohttp
async def foo(times):
 data = {'foo': 1}
 async with aiohttp.ClientSession() as session:
  for x in range(times):
   resp = await session.post('http://localhost:8080', json=data)
   if not x % 100:
    print(await resp.json())
loop = asyncio.get_event_loop()
loop.run_until_complete(foo(100000))
loop.close()

由于运行环境为Linux或macOS,可通过htop工具实时监控程序内存占用。启动服务端后,初始内存占用极低。

启动客户端后再次观察,发现内存持续攀升,即便客户端运行结束,内存也未随之释放。

客户端停止后的内存状态截图显示:

若客户端持续运行,内存将无限增长直至耗尽系统资源,最终导致进程被系统强制终止。

二、内存泄漏排查过程

上述示例程序逻辑简单,仅涉及基本的POST请求处理。但在实际复杂项目中,业务逻辑繁多,定位内存泄漏点并非易事。初期尝试了memory_profiler、objgraph及graphviz等工具,均未快速定位问题。最终发现Python标准库中的tracemalloc(文档地址:https://docs.python.org/3/library/tracemalloc.html)提供了有效解决方案。

借助该模块,我迅速定位到了内存泄漏的具体位置。

参照官方文档示例,我对代码进行了改造以验证泄漏源,修改后的服务端代码如下:

from aiohttp import web
import tracemalloc
async def hello(request):
 return web.json_response(await request.json())
async def get_info(request):
 snapshot2 = tracemalloc.take_snapshot()
 top_stats = snapshot2.compare_to(snapshot1, 'lineno')
 print(top_stats)
 return web.Response(text="ok")
if __name__ == '__main__':
 app = web.Application()
 app.add_routes(
  [
   web.post('/', hello),
   web.get("/get_info", get_info)
  ]
 )
 tracemalloc.start()
 snapshot1 = tracemalloc.take_snapshot()
 web.run_app(app)

需重点关注print(top_stats)的输出结果。

新增get_info路由,启动服务端并运行客户端。客户端结束后内存依然上涨且不释放。此时通过浏览器访问get_info路由,查看控制台打印内容。输出结果按内存增长量排序,前几项即为重点关注对象(因数据量大,此处仅展示部分前缀数据)。

<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=56>,)> size=116500672 (+116500672) count=300004 (+300004)>,
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=604>,)> size=11400000 (+11400000) count=200000 (+200000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=472>,)> size=8000000 (+8000000) count=100000 (+100000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=353>,)> size=5500000 (+5500000) count=100000 (+100000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=352>,)> size=5300608 (+5300608) count=100001 (+100001)>,

以第一行数据为例,清晰显示web_response.py第56行代码导致内存增长最大。对于复杂项目,此方法可快速定位内存溢出源头。深入查看该行代码:

追踪至最终行,问题指向CIMultiDict类。

class CIMultiDict(MultiDict):
 def _title(self, key):
  return key.title()

观察发现CIMultiDict继承自MultiDict,由此推断问题根源在于MultiDict相关实现。

进一步确认,问题出在MultiDict库本身,该项目维护地址为:https://github.com/aio-libs/multidict。

查阅该库仓库时,发现已有其他用户报告相同问题,并提交了对应的Bug报告。

具体Issue链接为:https://github.com/aio-libs/multidict/issues/307

值得称赞的是,国外开发者响应迅速,aio-libs团队很快回应并修复了该问题。在最新版本4.5.2中,经测试已确认不再存在内存泄漏。

三、总结

此次排查过程暴露出我在问题查找方法及解决思路上的不足。但经过此次实践,未来再遇类似问题时,能够更快速地定位与处理。

同时,强烈建议开发者熟悉https://docs.python.org/3/library/tracemalloc.html中介绍的tracemalloc库,它在内存调试方面极具价值。

以上为本次Python内存泄漏问题排查与解决过程的分享,希望能为读者提供帮助。如有任何疑问,欢迎留言交流,小编将及时回复。感谢大家对本站的支持。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表