Preamble

Not every performance story is CPU. Some are memory: accidental retention of giant lists, duplicated caches, closures holding references to whole data frames, or lru_cache on unbounded argument spaces. cProfile and py-spy: Two Ways to See Where Python Spends Time covered cProfile and py-spy for time; January 2022 complements that with heap visibility.


tracemalloc: snapshots and diffs

tracemalloc (stdlib) records allocation traces. I take a snapshot before and after a suspect code path and compare them—top lines show where bytes grew. The overhead is real; I enable it in staging or targeted scripts, not blindly in every production worker.


memory_profiler: line-by-line RSS

memory_profiler with @profile annotates which lines allocate or retain in a function. It pairs well with the “deep getsizeof” intuition from early data structure posts—containers hide object graphs.


When RSS grows without throughput

If Kubernetes keeps OOMKilling a pod while QPS looks flat, measure before guessing “GC tuning.” Often the fix is one accidental global list or a cache without eviction—not a mystical interpreter bug.


Conclusion

Memory discipline mirrors Java heap analysis in long-running services. Java Records for DTOs and Immutable Carriers moves to records and immutability on the JVM—fewer shared-mutation surprises across threads.