@profile 不生效是因为未用 python -m memory_profiler 运行脚本;它仅是标记,需该模块入口触发采样;必须用 python -m memory_profiler script.py 启动,参数置于文件名后。

为什么 memory_profiler 的 @profile 不生效?
直接加装饰器却没输出,大概率是没用 python -m memory_profiler 启动脚本。Python 解释器默认不识别 @profile,它只是个标记,真正触发内存采样的是 memory_profiler 自己的执行入口。
实操建议:
- 不要用
python script.py运行,必须用python -m memory_profiler script.py - 如果脚本有命令行参数,参数要写在文件名之后:
python -m memory_profiler script.py --input data.json - 若想监控某函数而非全脚本,仍需启动时加
-f或--follow-children(视版本),否则子进程/线程内存不会被追踪
line_profiler 和 memory_profiler 能一起用吗?
能,但不能靠装饰器叠加——@profile 只会被其中一个模块识别。更可靠的方式是分两次运行:一次用 line_profiler 看耗时热点,一次用 memory_profiler 看内存增长行。
注意点:
立即学习“Python免费学习笔记(深入)”;
-
memory_profiler默认每 100ms 采样一次,对短函数可能漏掉瞬时峰值;可加-D 10(单位 ms)提高精度,但会显著拖慢执行 - 若函数内有循环且每次迭代都 new 对象,采样粒度不够会导致你只看到“整体涨了”,看不出哪次迭代分配最多
- 避免在 Jupyter 中用
%memit查看长期增长趋势——它只给总增量,没法定位到具体哪行反复分配
监控结果里 Mem usage 突增但找不到对应代码行?
常见于隐式对象创建:比如 json.loads() 返回新 dict、re.findall() 返回 list、甚至 str.split() 生成多个子串对象。这些调用本身只占一行,但背后分配可能远超预期。
排查建议:
- 把可疑函数拆成两步:先存返回值,再用该变量做后续操作。例如把
data = json.loads(s)拆成s_json = s; data = json.loads(s_json),让memory_profiler把分配动作“钉”在json.loads那行 - 检查是否用了
copy.deepcopy()或pandas.DataFrame.copy()——它们常被忽略,却是内存大户 - 留意
logging.debug()或print()里拼接字符串(如f"item={item}"),尤其在循环中,临时字符串对象会累积
为什么 memory_profiler 显示内存没释放,但 gc.collect() 后也没降?
不是没释放,是 Python 的内存管理机制导致的:CPython 把释放的内存块保留在私有堆里,供后续同大小对象复用,不还给操作系统。所以 memory_profiler 看到的“增长”可能是碎片化堆积,而非真实泄漏。
关键判断点:
- 用
psutil.Process().memory_info().rss对比进程 RSS 内存,如果 RSS 持续上涨,才是真泄漏;如果只在memory_profiler输出里涨,而 RSS 平稳,大概率是内部缓存或分配器行为 - 检查是否有全局 dict/list 缓存未清理,比如用
functools.lru_cache()却没设maxsize,或自己写的 cache 字典不断.append()却不淘汰 -
memory_profiler的--include-children在多进程场景下容易误报——子进程内存会计入父进程采样,实际并不共享
objgraph)和 GC 状态(gc.get_objects())才能闭环。


















