内存泄露这个事,第一次让我警觉是在一个线上服务隔三差五就崩掉的深夜。我排查了半天,最后 top 命令一看,那个 Python 服务的内存直接飙上天了。
你说奇怪吧,明明没有什么特别重的运算任务,逻辑也挺简单的。一开始我怀疑是哪里忘了释放资源,结果一查,还真是个小细节惹的祸:一个死循环里不断 append 对象到列表,结果这个列表压根没清空过。那一刻我才真正体会到,什么叫“内存慢性中毒”。
内存泄露到底是个啥?
先甩个定义:内存泄露(Memory Leak)指的是程序在申请内存之后忘记释放,导致这部分内存长期占用、无法回收。
虽然 Python 有垃圾回收机制(GC),表面上听起来“自动管理内存”,但这不代表你能高枕无忧。Python 的 GC 机制主要基于引用计数,当一个对象的引用计数为 0 时,内存才会被回收。
而如果你写了个引用环,或者你用某种方式偷偷藏了一堆对象的引用,那这些对象就永远活着,GC 根本管不着。
比如下面这个例子,就挺有代表性:
这个脚本你让它跑,内存就一路疯涨。为啥?因为 leaks 一直在保存着对象引用,导致 GC 永远不会回收这些 Leaky 实例。
Python 里怎么会“漏”?
说实话,Python 里的内存泄露不太像 C/C++ 那种“我申请了 malloc() 忘了 free()”,更多时候是逻辑没理清,导致对象一直被引用着。
常见场景有哪些?
-
1. 全局变量误用
全局变量没控制好,对象永远在那儿挂着不释放。 -
2. 闭包引用
闭包引用外部变量,结果引用链断不了,比如下面这样:
- 只要你保存了 inner,那 local_data 也一直活着。
-
3. 缓存没清理
用 LRU 缓存结果,忘记加 size 限制。尤其是 functools.lru_cache,默认是无限缓存。 -
4. 循环引用
A 引用 B,B 又引用 A,如果其中有 __del__ 方法,GC 甚至都懒得动它们。 -
5. 事件监听器没移除
比如你写 GUI 或者一些 WebSocket 的订阅者模式,监听器一直在监听,但你早就不需要了。
怎么发现内存泄露?
说到这里,我觉得最实用的技巧不是“改代码”而是“先定位”。很多时候你得先知道是哪段逻辑在偷偷占内存,否则就是盲人摸象。
我比较常用的方法有这些:
1. 内存快照
tracemalloc 是 Python 自带的模块,挺好用的:
这能帮你快速找出内存占用多的地方。
2. objgraph
这是我最喜欢的图形分析工具之一,可以画出对象的引用链:
你就能看见这个 some_obj 是被谁引用着,像一张蛛网一样。
3. memory_profiler
这个库可以帮你按函数粒度监控内存变化:
跑完之后,它会告诉你每行代码的内存消耗。
怎么避免呢?
1. 掌握引用链
养成习惯,每写一个新类,脑子里最好有张“谁引用谁”的图,特别是数据结构里互相引用的场景。
2. 少用全局变量
真的,有时候一个不注意就搞出一个“全局大粪池”,啥都往里扔,谁也不敢清。
3. 控制缓存大小
比如 functools.lru_cache(maxsize=128),千万别忘了设 maxsize。
4. 监听器记得清除
很多人用 PyQt 写界面或者用某种 pub-sub 模式的时候,监听器绑得欢,解绑的时候就偷懒了。结果一个窗口对象关了半天内存还是飙着。
5. 监控系统资源
用 psutil 定时去测当前 Python 进程的内存使用情况,一旦发现异常增长,立马出手干预。
我的看法
我觉得,内存泄露在 Python 项目里虽然没那么频繁,但一旦出现就特别隐蔽,属于“你以为你写得没问题,其实早就炸了”。尤其是做服务端开发,程序长时间运行,一点点泄露堆积到几天后,就能把整个服务拖死。真是防不胜防。
所以我建议,不管你用什么框架,Flask、Django 也好,FastAPI 也罢,长期运行的服务最好加上定期内存监控,配合 tracemalloc、objgraph 这种工具,一旦发现内存不对劲,别犹豫,开干。
还有一个我觉得挺重要的点就是“代码审查时不光看功能,也要顺便看看资源使用”。很多时候内存泄露就是你自己挖的坑,埋得太深,连你自己都忘了。
就像我上面那个“死循环往列表加对象”问题,看起来就是个小测试代码,谁会想到能把服务器撑爆?
现在我项目里专门有个“内存告警系统”,一旦进程内存超过阈值,立刻打日志、发邮件、自动拉内存快照。别等出事才开始查。
体验地址就没啥了,内存泄露这个事,得靠你手上的 Python 自己管起来。要是你现在就怀疑项目有问题,立刻试试上面的 tracemalloc 或 objgraph,可能你会发现惊喜(或者惊吓)。
以上就是“python面试题:内存泄露是什么?如何避免?”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/13122/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料