Heroku部署的Django应用内存不释放,疑似模型引发内存泄漏求助
Django应用Heroku内存泄漏排查思路与解决建议
一、精准内存定位工具使用
tracemalloc(Python内置)
- 在settings.py中启用:
import tracemalloc tracemalloc.start() - 在请求结束或定时任务中打印内存快照对比:
snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') print("[Top 10 memory allocations]") for stat in top_stats[:10]: print(stat) - 重点关注重复出现的大内存分配位置,比如第三方API响应处理、中间件逻辑、模型属性计算。
- 在settings.py中启用:
objgraph
- 安装后在可疑代码段前后生成对象增长报告:
pip install objgraphimport objgraph objgraph.show_growth(limit=10) # 打印对象类型增长情况 - 用来排查是否有特定类型对象(比如模型实例、API响应字典)持续累积未被GC回收。
- 安装后在可疑代码段前后生成对象增长报告:
Heroku本地排查
- 用
heroku ps:exec进入dyno,安装memory-profiler后对可疑函数做逐行内存分析:heroku ps:exec pip install memory-profiler python -m memory_profiler your_script.py
- 用
二、针对怀疑点的专项排查
1. 订阅检查中间件
- 临时验证:注释掉中间件,重启dyno后观察用户访问后的内存变化。如果内存不再飙升,说明中间件存在泄漏。
- 代码审计:检查中间件是否在全局作用域存储数据(比如模块级变量),是否在每次请求后把对象添加到未清理的列表/字典中,是否持有模型实例引用导致GC无法回收。
- 生命周期跟踪:在中间件的
__call__方法前后添加对象计数,比如跟踪用户/组织实例的数量变化,确认请求结束后这些实例是否被正常释放。
2. 第三方API调用
- 不需要主动用
del释放内存,但要排查:- 是否使用了全局
requests.Session对象?全局Session会持续持有连接和响应缓存,建议每个请求创建独立Session,或使用后调用session.close()。 - 是否把API返回的大响应体(比如JSON、二进制数据)存储在全局变量或持久化缓存中?只保留必要字段,避免存储完整响应。
- 检查第三方SDK是否存在内存泄漏,比如某些SDK会缓存请求结果到全局容器,可查看其文档或源码确认。
- 是否使用了全局
3. 模型@property与@cached_property
- @cached_property风险:每个模型实例都会缓存计算结果,如果存在大量活跃实例(比如被全局缓存、列表持有),会累积内存。建议:
- 对缓存的结果做大小检查,避免缓存大对象(比如包含大量关联数据的字典)。
- 考虑用Django的
cache.set()代替实例级缓存,设置合理过期时间,避免实例持续持有缓存数据。
- 普通@property:本身不会导致内存泄漏,但如果@property内部执行了耗时查询或创建了大对象,且这些对象被其他地方引用,会间接导致内存占用。检查@property是否返回了不必要的大对象,是否可以优化为返回精简数据。
三、通用内存优化措施
Gunicorn配置优化
- 添加
--max-requests 1000参数,让worker处理指定数量请求后自动重启,避免内存泄漏累积:gunicorn --workers 2 --max-requests 1000 your_project.wsgi - 搭配
--max-requests-jitter 200让重启时间随机,避免所有worker同时重启影响服务。
- 添加
数据库查询优化
- 用
values()/values_list()替代完整模型实例查询,只获取需要的字段:# 代替 Organization.objects.filter(user=request.user) org_sub_status = Organization.objects.filter(user=request.user).values('subscription_status').first() - 禁用Django的自动事务管理(如果不需要),避免连接和事务对象残留。
- 用
全局变量与缓存清理
- 检查模块级变量是否在请求中被持续修改(比如列表append数据),这类变量会在worker生命周期内持续占用内存。
- 对Django缓存中的大对象设置过期时间,避免永久占用内存。
静态资源处理
- 确保媒体文件/静态文件通过Heroku的静态资源服务(比如Whitenoise)处理,避免Django进程加载大文件到内存。
内容的提问来源于stack exchange,提问作者Austin Grandt
相关产品推荐
相关产品推荐

