How does memory management and garbage collection work in Python?
CPython uses two mechanisms together.
Reference counting is the primary one. Every object tracks how many references point at it; when the count hits zero the memory is freed immediately. This is why an object's __del__ often runs the instant a variable goes out of scope, and why memory is reclaimed promptly.
A generational cycle collector handles what reference counting cannot: reference cycles. If a.child = b and b.parent = a, both counts stay above zero even when nothing else refers to them. The collector periodically walks objects, finds unreachable cycles, and frees them. It uses three generations on the observation that most objects die young, so generation zero is scanned frequently and older generations rarely.
What this means in practice:
- Memory leaks in Python are usually not leaks in the C sense — they are references you forgot you were holding. A module-level cache that only grows, a list that accumulates, or a closure capturing a large object.
- Use
weakrefwhen you need to reference an object without keeping it alive — the standard fix for parent-child cycles and caches. - Diagnose with
tracemallocto compare allocation snapshots, orgc.get_objects()andobjgraphto find what is holding a reference.
Note: Freed memory is not always returned to the operating system — CPython keeps arenas for reuse, so RSS can stay high after a spike.





