Python Memory Management: Under the Hood
How CPython manages memory ference counting, garbage collection, memory pools, and why your Python program uses more RAM than you expect.
The Two-Layer System
CPython uses a two-layer memory management system:
- Reference counting e primary mechanism. Every object has a count of references pointing to it.
- Generational garbage collector ndles reference cycles that refcounting can’t detect.
Reference Counting
Every Python object has a ob_refcnt field. When it hits zero, the memory is freed immediately.
import sys
a = [] # refcount = 1
b = a # refcount = 2
print(sys.getrefcount(a)) # 3 (includes function arg)
del b # refcount = 2
del a # refcount = 1 → freed? No, getrefcount added 1
The problem: circular references.
class Node:
def __init__(self):
self.next = None
a = Node()
b = Node()
a.next = b
b.next = a # Cycle! Refcount never hits 0
del a, b # Memory leaked without GC
The Generational Collector
CPython’s GC uses three generations (0, 1, 2). New objects start in generation 0. If they survive a collection cycle, they’re promoted.
import gc
# Force collection of specific generation
gc.collect(generation=0)
# See GC stats
print(gc.get_stats())
# [{'collections': 100, 'collected': 800, 'uncollectable': 0}, ...]
Memory Pools (pymalloc)
For small objects (≤ 512 bytes), CPython uses its own allocator with arenas (256 KB), pools (4 KB), and blocks.
This is why del doesn’t always return memory to the OS e arena might have other live objects keeping it allocated.
Practical Tips
- Use
__slots__for classes with many instances iminates the per-instance__dict__ - Avoid circular references when possible e
weakreffor caches and callbacks - Profile with
tracemallocshows you exactly where memory is allocated - Generator expressions over list comprehensions for large datasets zy evaluation uses constant memory
# Bad: allocates entire list
total = sum([x*x for x in range(10_000_000)])
# Good: constant memory
total = sum(x*x for x in range(10_000_000))
Understanding these internals helps you write Python code that doesn’t mysteriously eat all your RAM.