Flask内存持续增长通常是开发者代码未释放引用所致,需用psutil确认泄漏、tracemalloc定位源头,重点排查全局缓存、未关闭数据库连接、重复添加日志handler等,并在teardown钩子中显式清理。

Flask 应用内存持续增长,psutil 看到 RSS 不断上升
这通常是内存泄漏的典型信号,但 Flask 本身不直接导致泄漏——问题多出在开发者代码里没释放的引用。先确认是否真泄漏:psutil.Process().memory_info().rss 每隔 30 秒打点记录,排除是缓存累积或 GC 延迟造成的假象。
重点排查方向:
- 全局字典/列表被不断
.append()或[key] = value,比如把用户 session 数据无清理地塞进app.config['CACHE'] - 使用了
functools.lru_cache但没设maxsize,尤其装饰了带动态参数的视图函数 - 数据库连接未关闭(如手动调用
create_engine后没dispose()),或 ORM 对象长期持有 session 引用 - 日志 handler 被重复添加(比如在
app.route内部调用logger.addHandler())
用 tracemalloc 定位 Python 层泄漏源头
tracemalloc 是标准库中最快上手的内存分配追踪工具,适合 Flask 这类长周期进程。关键不是“启动时开”,而是“在疑似泄漏后快照对比”。
实操建议:
- 在应用启动后(如
if __name__ == '__main__':前)调用tracemalloc.start(25),25 表示保存最多 25 层调用栈 - 触发几次请求后,调用
snapshot1 = tracemalloc.take_snapshot();再触发一批请求,再取snapshot2 - 用
snapshot2.compare_to(snapshot1, 'lineno')查看新增分配最多的文件行号——通常能直接定位到哪行dict.append()或json.loads()后没清空 - 注意过滤掉
venv/和site-packages/路径,聚焦自己写的模块
weakref 和 __del__ 在 Flask 中基本无效
别指望靠 weakref.WeakKeyDictionary 或重写 __del__ 来“自动清理”。Flask 的生命周期模型和 WSGI server(如 Gunicorn)的 worker 复用机制会让这些机制失效:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
-
__del__触发时机不可控,可能直到 worker 进程退出才执行,起不到及时释放作用 -
weakref对循环引用无解,而 Flask 中request→g→ 自定义对象 →request很容易形成环 - 更可靠的做法是显式清理:在
@app.teardown_appcontext或@app.teardown_request里手动del全局缓存项、关闭临时文件句柄、清空g上的非必要属性
Gunicorn worker 重启掩盖泄漏,但会暴露真实压力点
很多团队用 --max-requests=1000 让 worker 定期重启,以为“解决了泄漏”。其实只是把问题延后——泄漏越严重,worker 越早被杀,日志里频繁出现 Worker timeout 或 Killed 就是征兆。
真正该做的是:
- 把
--max-requests临时设为 0,让 worker 长时间运行,配合tracemalloc抓泄漏点 - 检查是否在
before_first_request里初始化了单例但没做线程安全保护,导致多个线程重复构造大对象 - 确认所有后台任务(如用
threading.Thread启动的)都设置了daemon=True,否则 worker 无法正常退出
最常被忽略的是:泄漏往往不在路由函数里,而在你 import 的某个工具模块中——那个模块悄悄维护了一个永不清理的 _cache = {}。查的时候,别只盯着 app.py。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










