0Pricing
Python Academy · 课时

垃圾回收器与循环引用

了解 gc 模块如何处理引用循环。

垃圾回收器与循环引用 是 CoddyKit 上的免费 Python Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。

为什么引用计数还不够

引用计数无法回收参与引用循环的对象——每个对象都持有对另一个对象的引用,因此任何一个对象的计数都不会降为 0。

# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect them

gc 模块

gc 是循环垃圾回收器。它会定期扫描被跟踪的对象,查找不可达的循环并将其回收。

import gc

gc.collect()   # trigger immediately
print(gc.garbage)   # objects with __del__ in cycles (cannot auto-collect)

分代回收

垃圾回收器使用三代。新对象从第 0 代开始。经历一次回收后仍然存活的对象会晋升。第 0 代的回收频率最高。

import gc

print(gc.get_count())     # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10)  # tune

使用 gc.get_referents 检测循环

检查一个对象引用了哪些对象,这有助于理解某个对象为何没有被回收。

import gc

a = []
b = [a]
a.append(b)

for ref in gc.get_referents(a):
    print(ref)   # shows b

gc.get_objects()

gc.get_objects() 会返回当前由垃圾回收器跟踪的所有对象。这对于查找内存泄漏很有用。

import gc

before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")

避免循环

请设计数据结构以避免循环:对反向引用使用弱引用,使用 ID 代替直接引用,或者在对象离开作用域前显式打破循环。

import weakref

class Node:
    def __init__(self, parent):
        # weakref avoids cycle:
        self.parent = weakref.ref(parent)

class Tree:
    def __init__(self):
        self.child = Node(self)   # no hard cycle

__del__ 与循环

包含 __del__ 且参与循环的对象无法被自动回收,而是会被放入 gc.garbage。

import gc

class Leaky:
    def __del__(self): pass

a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b

gc.collect()
print(gc.garbage)   # [Leaky, Leaky] — cannot collect

gc.freeze() — Python 3.7+

gc.freeze() 会冻结当前被跟踪的对象,使它们永远不会被回收。在启动后运行时间较长的进程中使用它,可以减少垃圾回收暂停。

import gc

# After all globals and imports are in place:
gc.freeze()   # no GC scanning for these objects ever again

# Useful for: gunicorn pre-fork, background servers

禁用和重新启用垃圾回收器

在确定不存在循环的紧密循环中,请禁用循环垃圾回收器;之后重新启用它,并强制执行一次回收。

import gc

gc.disable()
try:
    result = tight_loop_no_cycles()
finally:
    gc.enable()
    gc.collect()

isenabled 和 isfinalized

请使用 gc.isenabled() 检查垃圾回收器状态,并使用 gc.is_finalized(obj) 检测对象是否正在终结。

import gc

print(gc.isenabled())      # True by default
obj = object()
print(gc.is_finalized(obj))  # False (still alive)

内存泄漏模式

常见的 Python 内存泄漏原因包括:无限增长的全局缓存、未解除注册的事件监听器、包含 __del__ 的引用循环,以及泄漏引用的 C 扩展。

# Typical leaks:
global_cache = {}   # grows forever — use lru_cache with maxsize

class EventBus:
    listeners = []
    # Listeners hold references to objects:
    # @classmethod
    # def on(cls, fn): cls.listeners.append(fn)
    # Must call off() to detach

快速检查

参与引用循环且包含 __del__ 的对象会发生什么?

总结

循环垃圾回收器会处理引用计数无法处理的引用循环。它使用三代,并将存活对象晋升。请使用弱引用避免循环。循环中包含 __del__ 的对象会进入 gc.garbage。请在启动后使用 gc.freeze(),以减少长时间运行的服务器中的垃圾回收暂停。

常见问题解答

「垃圾回收器与循环引用」课时是免费的吗?

是的 — 「垃圾回收器与循环引用」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。

「垃圾回收器与循环引用」这节课中我会学到什么?

了解 gc 模块如何处理引用循环。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Python Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「垃圾回收器与循环引用」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Python Academy 课中编写并运行代码吗?

能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. CPython 引用计数
  2. 垃圾回收器与循环引用
  3. 使用 cProfile 和 line_profiler 进行性能分析
  4. 使用 tracemalloc 进行内存分析
← 返回 Python Academy