专栏进度:02 / 03 (性能调优专题)
如果说时间分析是优化“效率”,那么内存诊断就是保障“生存”。一个 CPU 占用高的程序只是慢,但一个内存溢出(OOM)的程序会直接导致系统崩溃。
一、 宏观监测:tracemalloc 定位泄露源
Python 内置的 tracemalloc 模块非常强大,它可以对比两个时间点的内存快照(Snapshot),告诉你在这段时间内,内存具体增加在了哪些代码行。
实战代码:
Python
importtracemalloc tracemalloc.start()— 你的业务逻辑代码
snapshot=tracemalloc.take_snapshot()top_stats=snapshot.sort_stats('lineno')forstatintop_stats[:10]:print(stat)经验:重点关注 size 增加最明显的行,那通常是大型列表、字典或者未关闭的文件句柄所在。
二、 行级诊断:memory_profiler
类似于上一篇的时间分析,如果你想知道一个函数内部每一行代码分别消耗了多少内存,memory_profiler 是不二之选。
实战用法:
安装:pip install memory_profiler
在函数上加 @profile。
运行:python -m memory_profiler my_script.py
你会发现: 原来代码中那个看似简单的 list.extend() 在处理大数据量时,会产生惊人的瞬时内存峰值。
三、 常见内存“凶手”排查清单
无限增长的全局集合:在全局作用域定义 list 或 dict 作为缓存,却忘记设置过期机制或容量限制。
闭包引起的循环引用:虽然 Python 的 GC 能处理大部分循环引用,但在某些涉及del方法的复杂对象中,仍可能失效。
大文件读取不当:一次性使用 f.read() 读取数 GB 的文件,而不是使用迭代器 for line in f。
四、 避坑指南:内存占用不等于泄露
内存池机制:Python 为了效率,在释放对象后并不会立刻将所有内存还给操作系统,而是保留在自己的内存池中。不要看到 RSS 指标没降就断定有泄露。
slots的妙用:如果你需要创建数百万个小实例(如坐标点),在类中定义slots可以省去每个实例dict的开销,内存最高可缩减 70%。