Learning outcomes
- Trace reference counting and cyclic GC sweeps
- Diagnose and fix memory leaks in web services
Mental model
Python memory management operates in two layers: Reference Counting for immediate deallocation when reference count hits zero, and a Generational Garbage Collector (Generations 0, 1, 2) to detect cyclical object graphs.
Theory
Every Python object contains a reference count header. When reference count reaches 0, CPython immediately returns memory to PyMalloc arenas. However, self-referencing objects (a.child = b; b.parent = a) form cyclic graphs that reference counting cannot clear. The cyclic GC periodically inspects Generation 0, 1, and 2 containers to break unreachable cyclic references.
import gc
class Node:
def __init__(self, name: str):
self.name = name
self.peer = None
def trigger_cyclic_gc():
gc.disable()
n1 = Node("Node1")
n2 = Node("Node2")
n1.peer = n2
n2.peer = n1
del n1
del n2
unreachable_count = gc.collect()
gc.enable()
return unreachable_count
Alternatives and trade-offs
- Manual
gc.collect(): Forces immediate cycle cleanup but pauses execution. gc.freeze()in Python 3.8+: Freezes pre-forked parent process memory so Copy-on-Write (CoW) memory pages are not dirtied during worker process forks.
Failure modes and misconceptions
__del__Destructors: In older Python versions, objects with custom__del__methods involved in cycles were uncollectable.- Global Caching Leaks: Storing items in un-bounded global dicts maintains positive reference counts permanently.
Decision scenario
In multi-process web worker deployments (Gunicorn/Uvicorn), call gc.freeze() after loading application code and before worker process forks to optimize Copy-on-Write memory sharing.
Learning outcomes
- Differentiate reference counting from cyclic generational garbage collection.
- Trace memory arena allocations in CPython (
PyMalloc). - Utilize
tracemallocandgc.freeze()for memory optimization in web services.
Trade-offs
Generational garbage collection cleans up circular references automatically, but un-tuned GC sweeps can introduce latency spikes during high-throughput requests.
Evidence assessment
Theory and decision mastery
Decision scenario
A developer observes a production FastAPI service consuming 4GB RAM after running for 24 hours, even though active traffic is low.
Which diagnostic procedure and fix isolates the memory leak?
Primary sources
- Python 3.12 AsyncIO Event Loop & Asynchronous I/O Specification — Python Software Foundation, verified 2026-07-22