Advanced 24 min readModule: Module 16: Memory Profiling: `tracemalloc`, GC Internals & `__slots__`
Memory Profiling: tracemalloc, GC & __slots__
Optimize Python memory consumption: tracing line-by-line allocations with `tracemalloc`, understanding Reference Counting and Cyclical GC generations, and slashing object memory footprint with `__slots__`.
What You Will Learn in This Lesson
- Python's dual memory management: Immediate Reference Counting + Cyclical Garbage Collector (Generations 0, 1, 2)
- Pinpointing memory leaks and allocation spikes using the `tracemalloc` module
- Why standard Python classes use dynamic `__dict__` and how `__slots__` reduces memory by 60%+
- Detecting reference cycles using the `gc` module (`gc.get_referrers()`, `gc.collect()`)
Introduction & Core Concept
Python uses Reference Counting as its primary memory management mechanism: when an object's reference count drops to zero, it is deallocated immediately. To resolve circular references (e.g. A references B, and B references A), CPython runs a generational cyclical garbage collector that scans objects across Generations 0, 1, and 2.
WHY DOES THIS MATTER IN THE REAL WORLD?
By default, every Python class instance maintains an internal `__dict__` dictionary for dynamic attribute storage, which consumes ~150 bytes per object. In applications holding millions of objects (such as graph nodes or cache records), declaring `__slots__` slashes memory consumption from gigabytes down to megabytes.
Syntax & Structure
python
import tracemalloctracemalloc.start() class OptimizedNode: __slots__ = ('id', 'value', 'parent')Memory Profiling with tracemalloc and __slots__ Comparison
pythonpython
123456789101112131415161718192021222324252627282930313233343536373839# Memory Optimization: tracemalloc & __slots__ Benchmarkimport sysimport tracemalloc# 1. Standard Class (Uses dynamic __dict__)class StandardUser:def __init__(self, user_id: int, username: str):self.user_id = user_idself.username = username# 2. Optimized Class with __slots__ (Eliminates __dict__ per instance!)class SlottedUser:__slots__ = ('user_id', 'username')def __init__(self, user_id: int, username: str):self.user_id = user_idself.username = username# Benchmark Memory Allocations with tracemalloctracemalloc.start()# Allocate 50,000 Slotted instancesslotted_users = [SlottedUser(i, f"user_{i}") for i in range(50000)]current, peak = tracemalloc.get_traced_memory()tracemalloc.stop()print("=== Python Memory Profiling Results ===")print(f"Memory for 50,000 Slotted Users: {peak / 1024 / 1024:.2f} MB")# Inspect instance memory size directlystandard_inst = StandardUser(1, "alex")slotted_inst = SlottedUser(1, "alex")std_size = sys.getsizeof(standard_inst) + sys.getsizeof(standard_inst.__dict__)slot_size = sys.getsizeof(slotted_inst)print(f"Standard Class Instance Size: {std_size} bytes (with __dict__)")print(f"Slotted Class Instance Size: {slot_size} bytes (Fixed descriptor array)")print(f"✅ Memory Savings: {((std_size - slot_size) / std_size) * 100:.1f}% reduction per instance!");
Line-by-Line Technical Breakdown
1CPython Cyclical Garbage Collection Generations: New objects are allocated into Generation 0. If they survive a GC collection cycle, they are promoted to Generation 1, and eventually to Generation 2 (long-lived objects). The collector runs less frequently on older generations to minimize CPU overhead.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[PYTHON]
PYTHON SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Creating circular references in classes with `__del__` methods in older Python versions, preventing garbage collection.
Circular strong references cannot be collected by reference counting alone. Use `weakref` for back-pointers in trees and graphs.
Incorrect / Antipattern
class Node: def __del__(self): pass # Can create uncollectable cyclesCorrect / Professional Solution
# Use weakref.ref for parent pointers to prevent circular strong reference cyclesIndustry Best Practices & Professional Standards
- Add `__slots__` to data model classes instantiated millions of times.
- Use `tracemalloc.take_snapshot()` before and after operations to find memory leaks.
- Use `weakref.WeakValueDictionary` for in-memory caches to allow automatic garbage collection.
Lesson Summary & Core Takeaways
- CPython combines Reference Counting with a 3-Generation Cyclical Garbage Collector.
- `tracemalloc` tracks line-by-line memory allocation differences in production scripts.
- `__slots__` replaces `__dict__` with compact memory arrays, saving 60%+ RAM per instance.