用 @profile 可精准定位函数中内存增量异常的代码行,需安装 memory-profiler 并用 python -m memory_profiler 运行;increment 为正且循环累积增长才提示泄漏,单次大幅增长多属正常分配。

怎么用 @profile 看清函数里哪行在悄悄吃内存
直接加装饰器就能看到每行执行前后的内存增量,比看总内存更准——因为很多“泄漏”其实是某几行反复申请没释放,不是整个函数的问题。
先装库:pip install memory-profiler,然后在目标函数上加 @profile,运行时用 python -m memory_profiler script.py 才会生效。不加 -m memory_profiler 运行,@profile 完全没反应,这是最常被卡住的地方。
-
@profile只对被装饰的函数生效,不会递归追踪它调用的其他函数(除非那些函数也加了) - 输出里
Mem usage是当前行执行完的内存,Increment才是这行新增的(负值表示释放),重点盯Increment持续为正的行 - 如果某行
Increment很大但只出现一次,大概率是正常分配;如果循环里每轮都 +1MB,那基本就是泄漏点了
memory_profiler 为什么有时候报错 AttributeError: 'NoneType' object has no attribute 'group'
这是 memory_profiler 解析源码时失败了,常见于:函数定义在交互式环境(如 IPython/Jupyter)、或用了动态生成的函数(exec、lambda 嵌套太深)、或源码文件被编辑后没保存就运行。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 确保函数写在 .py 文件里,且运行的是该文件(不是 copy-paste 到终端)
- 避免在装饰的函数里用
eval或exec动态执行含变量定义的代码 - Jupyter 中必须用
%load_ext memory_profiler+%memit或%mprun -f func_name,不能直接跑python -m memory_profiler
查长期运行服务的内存泄漏,memory_profiler 能不能持续监控
不能。它是一次性快照工具,适合定位“某次调用中哪行涨得猛”,不适合看“运行 2 小时后内存是否缓慢爬升”。想监长时间趋势,得换方式。
- 用
psutil.Process().memory_info().rss每隔几秒打点,写进日志,再画曲线——简单粗暴但有效 - 结合
gc.get_objects()看特定类的实例数量是否只增不减(比如[o for o in gc.get_objects() if isinstance(o, MyCache)]) -
memory_profiler的mprof子命令可后台采样:mprof run --include-children your_script.py,但采样间隔固定(默认 0.1s),高频服务可能压垮自己
为什么 @profile 显示某行内存涨了,但实际没对象泄露
Python 的内存管理有延迟,list.append() 或字符串拼接这类操作,可能触发底层内存池分配,但对应 Python 对象生命周期很短,GC 一跑就清了。这时候 Increment 是真涨了,但不是泄漏。
- 确认是不是泄漏,得看同一段代码重复执行时,内存是否逐轮累积上涨(比如循环 100 次,第 100 次的基线比第 1 次高很多)
- 留意
sys.getrefcount()或weakref.getweakrefs(),有时对象没被删是因为被意外强引用(比如注册到全局回调列表、闭包捕获、日志 handler 持有) - 字符串、bytes、小整数等有对象池机制,它们的“增长”往往不可怕;真正要盯的是自定义类实例、大型 numpy 数组、未关闭的文件句柄
真实泄漏往往藏在引用链里,@profile 只能告诉你“这里涨了”,但没法自动告诉你“谁还拿着它的引用”。得配合 objgraph 或手动查 gc.get_referrers() 往上翻两层。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










