内存优化核心在于减少对象冗余、控制缓存大小和采用流式响应:用__slots__压缩实例内存,避免动态属性;对大响应使用stream()而非json();按实际路径热度调优路由与响应缓存;禁用调试模式与全局缓存中间件,从源头切断循环引用。

直接上结论:内存占用每降 1GB,云服务器月成本约省 6 元;对 Sanic/Django/Flask 类应用,80% 的内存浪费来自对象冗余、缓存失控和响应加载方式不当——不是框架本身慢,而是默认用法没调优。
用 __slots__ 压缩实例内存,尤其在模型/请求上下文中
Python 实例默认带 __dict__,每个对象额外吃掉 56+ 字节基础开销。处理万级请求或用户实体时,这会快速累积成百 MB。
- 类定义中显式声明
__slots__ = ("id", "name", "status"),禁止动态属性,移除__dict__和__weakref__ - 避免在中间件、装饰器里临时挂载属性(如
request.user_cache = obj),改用局部变量或 contextvars - 若需兼容动态字段,用
dataclasses.field(repr=False)+__slots__混合方案,而非放弃__slots__
流式响应大文件或查询结果,别用 json() 或 text() 一次性加载
调用 json(data) 会把整个 data 序列化为字符串再塞进响应体,内存峰值 = 数据大小 × 2~3 倍(序列化副本 + 响应缓冲)。
- 对数据库游标、日志行、CSV 导出等场景,用
stream()响应:@app.route("/export")下写异步生成器函数 - Sanic 中示例:
return stream(lambda r: async_for row in db.fetch(): await r.write(...)) - Flask 可用
Response(stream_with_context(gen));Django 则推荐StreamingHttpResponse - 注意:流式响应不能设
Content-Length,客户端需支持 chunked encoding
路由与响应缓存别硬编码大小,按实际负载调 ROUTER_CACHE_SIZE 和 max_age
Sanic 默认 ROUTER_CACHE_SIZE=1000,Django REST Framework 默认全量缓存视图输出——缓存不是越多越好,过大会吃光内存,过小则频繁失效反增 CPU。
- 查线上访问日志,统计 Top 100 路径的覆盖率,设
ROUTER_CACHE_SIZE为该数量 × 1.5(例如只 200 条路径高频访问,就别留 1000) -
@cache(max_age=300)比@cache(max_age=86400)更安全:短 TTL 让内存自然释放,避免冷数据长期驻留 - 禁用全局响应缓存中间件(如 Django 的
UpdateCacheMiddleware),改用视图级cache_page控制粒度
别让 gc.collect() 成为救命稻草,优先切断循环引用源头
看到内存缓慢上涨?gc.collect() 能临时缓解,但治标不治本。真正的问题常藏在闭包、信号监听器、长生命周期对象持有的回调里。
- 用
objgraph.show_growth()对比两个时间点的对象增长,重点盯function、method、dict类型暴增 - 注册事件回调时,用
weakref.ref(handler)替代强引用,防止 handler 持有 instance 导致整条链无法回收 - 数据库连接池、Redis 客户端等全局单例,确保其内部不偷偷存 request 或 session 引用
最易被忽略的是:生产模式启动参数和日志级别。一个 --debug 开关或 LOG_LEVEL=DEBUG,会让 Sanic/Django 把每条 SQL、每个中间件进出都记进内存 buffer,持续数小时不 flush——这种“隐形内存泄漏”比代码问题更难排查。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











