Reference counting
How Python Tracks Memory
Section titled “How Python Tracks Memory”Python uses reference counting as its primary memory management strategy. Every object on the heap carries an internal counter, the reference count, that tracks exactly how many variables, data structures, or internal references are currently pointing to it.
The rules are simple:
- A new reference to an object increments the counter
- A reference is removed (variable deleted, goes out of scope, reassigned), the counter decrements
- When the counter reaches zero, no code can possibly reach the object anymore, Python immediately frees the memory
a = [1, 2, 3] Heap ──── a ─────────────────────► [ list: 1, 2, 3 ] refcount = 1
b = a Heap ──── a ─────────────────────► [ list: 1, 2, 3 ] refcount = 2 b ─────────────────────► ▲
c = a Heap ──── a ─────────────────────► [ list: 1, 2, 3 ] refcount = 3 b ─────────────────────► ▲ c ─────────────────────► ▲
del b Heap ──── a ─────────────────────► [ list: 1, 2, 3 ] refcount = 2 c ─────────────────────► ▲
del c del a Heap ──── [ list: 1, 2, 3 ] refcount = 0 → freed immediately ✓Verifying with sys.getrefcount()
Section titled “Verifying with sys.getrefcount()”Python exposes the reference count of any object via sys.getrefcount().
import sys
a = [1, 2, 3]print(sys.getrefcount(a)) # 2 — one for a, one for getrefcount's argument
b = aprint(sys.getrefcount(a)) # 3 — a, b, getrefcount's argument
c = aprint(sys.getrefcount(a)) # 4 — a, b, c, getrefcount's argument
del bprint(sys.getrefcount(a)) # 3 — b removed, back to a, c, argument
del cprint(sys.getrefcount(a)) # 2 — c removed, back to a, argument
del a# refcount hits 0 — object freed from memory immediatelyWhy It Matters
Section titled “Why It Matters”Reference counting has two important properties:
-
Immediacy: memory is freed the instant the last reference is removed, not at some unpredictable future time. This makes Python’s memory management deterministic and predictable for most cases.
-
Limitation: reference counting alone cannot handle circular references, where two objects reference each other and neither ever reaches zero. Python handles this with a supplementary cyclic garbage collector that runs periodically to detect and free these cycles:
# A circular reference — neither object ever reaches refcount 0a = {}b = {}a["b"] = b # a references bb["a"] = a # b references a
del adel b# Both still have refcount 1 — neither freed by reference counting alone# The cyclic garbage collector will find and free theseThe key insight is that del a and del b only remove the stack references, the arrows from variable names
to objects. The objects themselves still hold internal references to each other, keeping both counts at 1.
| Reference Counting | Cyclic GC | |
|---|---|---|
| Triggers | Every reference change | Runs periodically |
| Speed | Immediate | Delayed |
| Handles cycles | ❌ No | ✅ Yes |
| Overhead | Per operation | Batch |