SDET / SWE · Technical Fundamentals

TikTok Python 内存泄漏:可达引用、循环回收与资源释放

区分业务持续持有、循环对象和原生分配,按稳定负载定位增长,避免只看RSS断言泄漏。

题目重点:区分业务持续持有、循环对象和原生分配,按稳定负载定位增长,避免只看RSS断言泄漏。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 语言层行为与解释器实现分开;CPython引用计数/循环GC不当作所有Python实现保证。
  • 诊断示例只用内存对象,不提供某生产应用已有泄漏的结论。

解题路线

  1. 有GC仍会出现逻辑泄漏:全局缓存、队列、回调注册、日志/异常引用持续保持本应释放对象可达,垃圾回收器不能删除仍可达的业务状态。
  2. 循环引用并不自动等于永久泄漏,支持循环GC的实现能回收很多不可达循环。反之可达对象即使没有循环也不会被回收;排查不能只搜两个对象互相引用。
  3. 建立可重复负载与稳定阶段,观察活跃对象数、分配栈和保留引用链,区分一次性预热、无界缓存、批次堆积与正常峰值。单次RSS高不证明对象还活着,分配器可能保留内存供复用。
  4. Python层工具可跟踪分配和对象引用,原生扩展、GPU缓冲或文件连接可能在其观察范围外;分别测Python heap与进程/外部资源指标,不把看不到的分配宣称不存在。
  5. 治理按原因:缓存限制容量/TTL,队列做背压,回调取消注册,context manager关闭文件/连接,避免永久保存traceback或大闭包;weakref只适合允许对象自然消失的关系,不是强行解所有泄漏。
  6. 测试反复创建/释放后是否趋稳,配合weakref验证特定对象可回收,再检查异常和取消路径。不要在每次请求强行gc.collect代替修正持有关系,其暂停还会伤尾延迟。

手动走一遍

对象self指向自己后删除外部名字,支持循环GC的运行时可在collect后释放;同对象仍留在cache列表中则collect不会释放,清空cache后才可能回收。示例已用weakref核对两条路径。

规模与取舍

引用追踪和完整GC可能扫描大量对象,成本取决于存活图与实现;缓存空间受容量约束才能有界。诊断应记录稳定负载的增长率与尾延迟,而非宣称统一O(1)。

易错点与追问

容易失分的边界

  • Python有GC所以不可能泄漏。
  • 循环引用必然永不回收。
  • RSS不下降就确定泄漏。

继续深挖

  • 原生扩展泄漏怎么分层定位?
  • 异常traceback为什么能保留大对象?
  • 缓存/队列怎样建立内存预算?

技术参考

  • Python gc:技术语义参考,具体题目按本文约定。
✓已复制微信号!