Mastering Memory Leak Detection in Python Using Memray

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Background: The 2 AM OOM Killer Nightmare

Imagine you’re running a Python data processing system. Everything is stable until the record count hits 100,000. At exactly 2 AM, your phone rings: the server has crashed. Checking the logs, you see the haunting words: Out of Memory (OOM) Killer.

The OS took it upon itself to “execute” the application because it devoured 16GB of RAM in minutes. At this point, tools like top or psutil only tell you that RAM is increasing; they are powerless to pinpoint the specific line of code or object hogging the memory. After hours of struggling, I found Memray. This tool is a true game-changer.

Why do legacy tools often disappoint?

In the past, memory_profiler was the go-to choice. However, it slows down programs by 10 to 50 times. Its fatal flaw is the inability to see memory consumed by C extensions like NumPy, Pandas, or database drivers. Meanwhile, Memray is written in C++, allowing it to track both Python and native code with extremely low overhead.

Installing Memray in a Flash

Memray is an open-source product from Bloomberg. It works best on Linux. If you’re on macOS, you might face some feature limitations. Windows users can run it smoothly via WSL2.

pip install memray

For strict production environments, ensure you have python3-dev installed. This prevents compilation errors for extension components midway through.

Profiling Code Without “Polluting” the Project

What I love most is that Memray doesn’t force you to modify your source code. You don’t need to insert decorators or add import statements to your production files. Everything is handled externally.

1. Running a Basic Profile

Suppose you need to check processor.py. Simply execute the following command:

python3 -m memray run processor.py

Memray will generate a binary file containing the full memory allocation history. The filename usually follows the format memray-processor.py.<pid>.bin.

2. Reading the Flamegraph Report

Binary data is difficult to read with the naked eye. Convert it into a visual HTML format using the following command:

python3 -m memray flamegraph memray-processor.py.<pid>.bin

Open the generated HTML file in your browser, and you’ll see a memory allocation map. The wider the blocks, the more RAM that function consumes. You can click directly on each block to see the exact line of code causing the waste.

In Practice: Hunting Down “Hidden” RAM Hogs

Consider a real-world example: a log processing script leaking memory because it stores data in a global list without clearing it.

# leak_example.py
import time

def process_data():
    # Simulate data accumulation causing a leak
    cache = []
    for i in range(10000):
        cache.append(f"Record_{i}" * 100)
    return len(cache)

if __name__ == "__main__":
    for _ in range(10):
        process_data()
        time.sleep(0.5)

Instead of waiting for the script to finish, you can use the --live feature to observe it immediately:

python3 -m memray run --live leak_example.py

A professional terminal interface will appear, listing the functions consuming the most RAM in real-time. You’ll immediately see the process_data function continuously driving up memory usage without it ever dropping.

Deep Analysis with Table View

If the Flamegraph makes your head spin, try the Table view:

python3 -m memray table memray-leak_example.py.<pid>.bin

This table provides three key metrics: Total Memory, Own Memory, and Allocation Count. Experience shows that sometimes the Allocation Count is more important than the total size. Creating millions of small objects creates significant overhead for Python to manage, slowing down the system considerably.

Real-world Memory Optimization Tips

After handling many tough OOM cases, I’ve distilled three golden rules for using Memray:

  • Always enable Native mode: Use the --native flag when working with Pandas or Machine Learning. Without it, you’ll miss the massive chunk of memory residing in the C/C++ layer.
    python3 -m memray run --native script.py
  • Hunt for Peak Memory: Sometimes RAM doesn’t increase continuously (leak) but spikes (peak) and then drops. Memray helps you identify peak moments so you can replace a list comprehension with a generator, potentially reducing RAM from several GBs to just a few MBs.
  • Save snapshots: Keep report files from before and after optimization. The feeling of watching the RAM chart shrink after a code fix is incredibly satisfying and serves as the best proof of your work’s impact.

Memray is more than just a debugging tool. It helps you understand how Python operates under the hood. If your project is consuming an unusual amount of RAM, install Memray today. Wishing you peaceful nights of sleep, free from OOM Killer worries!

Share: