CPython Reference Counting
Understand how CPython tracks object lifetimes with refcounts.
CPython Reference Counting is a free Python Academy lesson on CoddyKit — lesson 1 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.
What Is Reference Counting?
CPython tracks every object's reference count. When the count drops to 0, the object is immediately deallocated. This is Python's primary memory management mechanism.
import sys
x = [1, 2, 3]
print(sys.getrefcount(x)) # 2 (x + getrefcount arg)
y = x
print(sys.getrefcount(x)) # 3sys.getrefcount()
sys.getrefcount(obj) returns the current reference count. Note: calling it adds 1 (the function argument reference).
import sys
a = "hello"
print(sys.getrefcount(a)) # baseline (may be high for interned strings)
b = a
print(sys.getrefcount(a)) # one more
del b
print(sys.getrefcount(a)) # back to baselineIncrement and Decrement
Each assignment increments; each deletion or reassignment decrements. When the count reaches 0, CPython calls the object's deallocator.
x = [] # refcount = 1
y = x # refcount = 2
z = [x] # refcount = 3 (list holds a reference)
del y # refcount = 2
z.clear() # refcount = 1
del x # refcount = 0 → deallocatedObject Interning
CPython interns small integers (-5 to 256) and many string literals, reusing the same object. is comparisons reveal this.
a = 256
b = 256
print(a is b) # True (interned)
c = 257
d = 257
print(c is d) # may be False (not interned)Weak References
Weak references do not increment the reference count. Useful to avoid preventing garbage collection of objects you want to observe but not own.
import weakref
class BigObject:
pass
obj = BigObject()
ref = weakref.ref(obj)
print(ref()) # <BigObject instance>
del obj
print(ref()) # None (object was collected)Reference Cycles
If A references B and B references A, neither reaches 0 even when both are unreachable. CPython's cyclic GC detects and collects these.
a = []
b = [a]
a.append(b) # cycle: a → b → a
del a, b
# Both are unreachable but refcount > 0
# The cyclic GC collects themThe gc Module
The gc module provides the cyclic garbage collector. It runs automatically but can be triggered manually or disabled.
import gc
gc.collect() # force a collection cycle
print(gc.get_count()) # (gen0, gen1, gen2) allocation counts
print(gc.get_threshold()) # when each generation is collectedMemory Pools (pymalloc)
CPython allocates small objects (< 512 bytes) from its own memory pool (pymalloc), which is faster than calling malloc for every small object.
# You do not call pymalloc directly — CPython uses it internally
# for all Python-level object allocations
# The pool pre-allocates large memory arenas
# and carves them into pools of fixed-size blocks__del__ Finalizers
__del__ is called when an object's reference count reaches 0 (or later if in a cycle). Avoid relying on it for critical cleanup — use context managers instead.
class Resource:
def __del__(self):
print(f"Freeing {self}")
# Better: use context manager
class SafeResource:
def __enter__(self): return self
def __exit__(self, *a): self.cleanup()Compact Objects with __slots__
Objects with __slots__ use less memory and fewer allocations, reducing GC pressure.
import sys
class Reg:
def __init__(self, x, y): self.x, self.y = x, y
class Slotted:
__slots__ = ("x","y")
def __init__(self, x, y): self.x, self.y = x, y
print(sys.getsizeof(Reg(1,2))) # ~48 + dict overhead
print(sys.getsizeof(Slotted(1,2))) # ~48 (no dict)Disabling the Cyclic GC
For programs that do not create reference cycles, disabling the cyclic GC can improve throughput.
import gc
gc.disable() # no cyclic GC — objects are only freed by refcounting
# Use only if you KNOW no cycles are created
# (pure functional/immutable data)
gc.enable() # re-enableQuick Check
When does CPython immediately deallocate an object?
Recap
CPython tracks objects via reference counts. Count reaches 0 → immediate deallocation. Reference cycles are handled by the cyclic GC (gc module). Avoid __del__ for critical cleanup; use context managers. Use __slots__ to reduce per-object memory.
Frequently asked questions
Is the “CPython Reference Counting” lesson free?
Yes — the full text of “CPython Reference Counting” 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 “CPython Reference Counting”?
Understand how CPython tracks object lifetimes with refcounts. 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 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “CPython Reference Counting” 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.