那个深夜,我的Python程序偷偷把内存吃光了

引用 gc 循环 __del__ 内存
发布于 2026-06-09
106

我们非常重视原创文章,为尊重知识产权并避免潜在的版权问题,我们在此提供文章的摘要供您初步了解。如果您想要查阅更为详尽的内容,访问作者的公众号页面获取完整文章。

扫码阅读
手机扫码阅读

文章主旨:通过剖析Python引用循环导致的内存泄漏问题,强调弱引用和手动断链是解决循环引用的关键,并提醒慎用__del__方法。

关键要点:

  • Python引用计数管理内存时,引用循环(A引用B,B引用A)会导致计数无法归零,内存无法释放。
  • gc模块的标记-清除算法可处理循环引用,但若循环中存在定义了__del__方法的对象,gc会放弃回收,导致内存泄漏。
  • 打破循环的两种有效方案:使用weakref弱引用(不增加计数),或手动断链(如close()方法中将引用置None)。
  • 排查循环引用的工具:gc.get_referrers()/gc.get_referents()objgraph可视化引用图、tracemalloc追踪内存分配。
  • gc调优建议:调整阈值、手动控制回收时机、禁用__del__采用上下文管理器;free-threading模式下gc停顿可能更明显。

内容结构:

  1. 引言:通过凌晨监控告警引出Python内存占用异常,类比“两个灯互相供电”,点明主题是引用循环。
  2. 引用计数机制介绍:说明引用计数原理(sys.getrefcount),指出其无法处理循环引用的缺陷。
  3. 循环引用示例:Node类互相引用导致计数不归零,类比“两灯互供电”。
  4. gc模块机制:标记-清除算法与分代回收(阈值(700,10,5)),手动触发gc.collect()
  5. 真凶__del__:详细分析连接池场景下Connection.__del__调用pool.return_connection导致gc不敢回收循环垃圾。
  6. 解决方案:弱引用(weakref.ref)和手动断链(close()方法置None)。
  7. 排查工具:gc.get_referrersobjgraph.show_backrefstracemalloc
  8. gc调优:调整阈值、手动控制、禁用__del__、free-threading对gc停顿的影响。
  9. 结尾:作者实际修复方案(改为close()+弱引用),并养成习惯避免互相引用。

文章总结:本文以生动比喻和真实案例,系统讲解了Python引用循环的内存泄漏成因、排查方法和解决方案,建议开发者慎用__del__,善用弱引用和上下文管理器。

Python学习杂记