编程后端开发人员

解释一下 Python 中的内存管理是如何工作的,包括垃圾回收器。什么是循环引用,Python 是如何处理它们的?

用 Hintsage AI 助手通过面试

答案

在 Python 中,内存是自动管理的:在创建对象时会分配内存,而当没有其他对象引用该对象时,内存会被释放。主要策略是 引用计数:每个对象都有与之关联的引用计数,当引用计数降为零时,对象会被删除。

然而,可能会出现 循环引用,即对象相互引用,导致它们的引用计数永远不会降为零,即使从外部代码中删除了引用。

为了处理这一问题,Python (CPython) 中有一个 垃圾回收器(garbage collector, GC),它会检测无法从根对象访问的对象循环,并将其删除。可以通过 gc 模块来管理垃圾回收器。

循环的示例:

class Node: def __init__(self): self.ref = None n1 = Node() n2 = Node() n1.ref = n2 n2.ref = n1 del n1, n2 # 对象仍然存在,因为存在循环引用!

有陷阱的问题

问题: “如果调用 del obj,内存总是会立即释放。 是真的?”

答案: 不!操作 del 只是删除了对对象的一个引用。如果在其他地方(明确或循环)仍然存在其他引用——内存不会释放,直到引用计数等于零和/或 GC 解除循环。示例:

import gc class A: pass x = A() y = x del x # y 仍然有引用,对象仍然存在! del y # 只有现在对象才可能被删除

由于对该主题细节不清楚而导致的实际错误的示例


故事

在大文件处理系统中,内存中保持了相关对象的引用。在删除外部引用后,内存仍然没有被释放。原因是循环引用没有及时被清除,有时由于存在析构函数 __del__ 根本没有被清除。


故事

在长寿命的服务器进程中,经常会创建大型临时结构与循环图。开发人员没有关闭 GC 的临时调试输出,因此会出现罕见但明显的延迟。被诊断为“性能降低”。结果发现是频繁调用垃圾回收器造成的。


故事

在将大型项目移植到 PyPy(替代解释器)时,旧代码依赖于 CPython 的即时内存清理行为通过引用计数——而在 PyPy 中,对象被删除和清理并不是立即的,而是根据 GC 的“意愿”,这导致了打开文件和网络连接时的不可预测行为。