Vì reference counting không xử lý được vòng tham chiếu.
Hai object trỏ vào nhau thì count của cả hai không bao giờ về 0, dù không còn ai bên ngoài giữ chúng.
import gc
class Node:
def __init__(self): self.peer = None
a, b = Node(), Node()
a.peer, b.peer = b, a # cycle
del a, b # refcount never reaches 0
gc.collect() # cyclic collector reclaims the pairCPython vì thế chạy hai cơ chế: refcount giải phóng tức thời và cyclic GC kiểu mark-and-sweep theo thế hệ (3 generation, gen0 quét thường xuyên nhất). Vòng tham chiếu rất hay xuất hiện ở doubly-linked list, quan hệ parent/child, cache giữ callback, hoặc object bắt exception rồi giữ traceback.
Công cụ chẩn đoán: gc.collect() trả về số object thu hồi; gc.get_referrers(obj) cho biết ai đang giữ object; objgraph/tracemalloc để soi rò rỉ thật.
weakref giữ tham chiếu không làm tăng refcount — object vẫn được giải phóng bình thường, còn weakref trả về None sau đó. Dùng khi:
- Cache/registry không được giữ object sống (WeakValueDictionary).
- Con trỏ ngược parent trong cây, để không tạo vòng.
- Observer/listener không được giữ subscriber sống mãi.
Lưu ý cuối: bộ nhớ được thu hồi vào allocator nội bộ (pymalloc arena) chứ không chắc trả ngay về OS — RSS trong container có thể vẫn đứng yên sau khi giải phóng.