0Pricing
Python Academy · Lesson

The Garbage Collector and Cyclic References

Learn how the gc module handles reference cycles.

The Garbage Collector and Cyclic References is a free Python Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Reference Counting Is Not Enough

Reference counting cannot collect objects involved in reference cycles — each holds a reference to the other, so neither count drops to 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

The gc Module

gc is the cyclic garbage collector. It periodically scans tracked objects for unreachable cycles and collects them.

import gc

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

Generational Collection

The GC uses three generations. New objects start in gen 0. Objects that survive a collection are promoted. Gen 0 is collected most frequently.

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

Detecting Cycles with gc.get_referents

Inspect which objects an object refers to — useful for understanding why something is not being collected.

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() returns all objects currently tracked by the GC. Useful for finding memory leaks.

import gc

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

Avoiding Cycles

Design data structures to avoid cycles: use weakrefs for back-references, use IDs instead of direct references, or break cycles explicitly before objects go out of scope.

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__ and Cycles

Objects with __del__ involved in a cycle cannot be auto-collected. They are placed in gc.garbage instead.

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() freezes currently-tracked objects so they are never collected. Useful in long-running processes after startup to reduce GC pauses.

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

Disabling and Re-enabling GC

Disable the cyclic GC in tight loops where you know no cycles exist, then re-enable and force a collection afterwards.

import gc

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

isenabled and isfinalized

Check GC state with gc.isenabled() and detect if an object is being finalized with gc.is_finalized(obj).

import gc

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

Memory Leak Patterns

Common Python memory leak causes: global caches that grow without bounds, event listeners not detached, cyclic references with __del__, and C extensions leaking references.

# 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

Quick Check

What happens to objects with __del__ that are involved in a reference cycle?

Recap

The cyclic GC handles reference cycles that refcounting cannot. It uses three generations and promotes survivors. Avoid cycles with weakrefs. Objects with __del__ in cycles land in gc.garbage. Use gc.freeze() after startup to reduce GC pauses in long-running servers.

Frequently asked questions

Is the “The Garbage Collector and Cyclic References” lesson free?

Yes — the full text of “The Garbage Collector and Cyclic References” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.

What will I learn in “The Garbage Collector and Cyclic References”?

Learn how the gc module handles reference cycles. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Python Academy?

No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “The Garbage Collector and Cyclic References” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Python Academy lesson?

Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. CPython Reference Counting
  2. The Garbage Collector and Cyclic References
  3. Profiling with cProfile and line_profiler
  4. Memory Profiling with tracemalloc
← Back to Python Academy