🐍 CPython Internals: Memory Management, GC, and __slots__
To write high-performance Python systems handling millions of records, you must understand how the default CPython runtime allocates, manages, and reclaims memory.
🧮 1. Primary Mechanism: Reference Counting
CPython's fundamental memory management mechanism is Reference Counting:
Every Python object has an internal ob_refcnt field.
When a variable or container references the object, the count increments.
When a reference goes out of scope or is deleted with del, the count decrements.
As soon as ob_refcnt == 0, memory is immediately deallocated!
1import sys
2
3x =[1,2,3]
4# Note: sys.getrefcount() temporarily creates 1 additional reference as an argument:
Reference counting has one critical flaw: Cyclic References (when Object A points to Object B, and Object B points back to Object A). Even if all external references are deleted, their internal reference counts never drop to 0!
To prevent memory leaks from cycles, Python includes a Generational Garbage Collector that runs periodically:
It tracks container objects across 3 generations (Gen 0, Gen 1, Gen 2).
Young objects start in Gen 0; survivors of collection cycles are promoted.
1import gc
2
3classNode:
4def__init__(self, val):
5 self.val = val
6 self.neighbor =None
7
8# Create a cyclic reference:
9a = Node("A")
10b = Node("B")
11a.neighbor = b
12b.neighbor = a
13
14del a
15del b # Neither reference count is zero due to cyclic mutual reference!
16
17# Force an immediate full garbage collection run:
18unreachable = gc.collect()
19print(f"Reclaimed {unreachable} cyclic objects from memory.")
🚀 3. Massive Memory Savings with __slots__
By default, every Python class instance maintains an internal __dict__ to allow dynamic attribute assignment. While flexible, this dictionary consumes substantial memory overhead (typically 150–200 bytes per instance).
When instantiating millions of objects (such as financial records or coordinates), defining __slots__ eliminates the __dict__, storing attributes in a compact C-level array:
18# ❌ AttributeError: 'SlottedPoint' object has no attribute 'z'
19# p_slotted.z = 30.0
Interactive Quiz• Knowledge Check
Practice Quiz
Question 1
What is the primary, immediate memory reclamation mechanism in CPython?
Question 2
Why does Python need the gc module in addition to reference counting?
Question 3
How many generations are used in Python's cyclic garbage collector?
Question 4
What is the primary performance benefit of defining __slots__ in a class?
Question 5
What happens when you attempt to assign an attribute not listed in __slots__ to a slotted instance?
🚀 What's Next?
In the next lesson, we will combine our magic method mechanics to build Project: Vector Class with Overloaded Operators (2: Magic Methods & Operator Overloading).