QUICK START:HTMLCSSJavaScriptTypeScriptPythonSQLReactNext.jsNode.jsLinux & UbuntuKotlinSwiftC# / .NETJavaGoRustC++DSASystem DesignDevOpsCybersecurityAI / ML
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 tracemalloc
tracemalloc.start()
class OptimizedNode:
__slots__ = ('id', 'value', 'parent')

Memory Profiling with tracemalloc and __slots__ Comparison

python
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Memory Optimization: tracemalloc & __slots__ Benchmark
import sys
import tracemalloc
# 1. Standard Class (Uses dynamic __dict__)
class StandardUser:
def __init__(self, user_id: int, username: str):
self.user_id = user_id
self.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_id
self.username = username
# Benchmark Memory Allocations with tracemalloc
tracemalloc.start()
# Allocate 50,000 Slotted instances
slotted_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 directly
standard_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 Code

Common 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 cycles
Correct / Professional Solution
# Use weakref.ref for parent pointers to prevent circular strong reference cycles

Industry 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.