Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Profiling with memory_profiler | Profiling and Leak Detection
Python Memory Management

Profiling with memory_profiler

Swipe to show menu

tracemalloc works from inside your code. memory_profiler takes a different approach – it decorates functions and reports memory usage line by line, showing exactly how much memory each line adds or frees. It is the most readable tool for understanding the memory profile of a specific function.

Installation

pip install memory-profiler

The @profile Decorator

Add @profile to any function and run the script with python -m memory_profiler your_script.py. Each line is annotated with memory usage and the increment from the previous line:

1234567891011121314151617181920212223
from memory_profiler import profile @profile def build_sales_report(): # Loading raw sales data sales_records = [ {"region": f"region_{record_id % 10}", "revenue": record_id * 150.0} for record_id in range(100000) ] # Grouping by region region_totals = {} for record in sales_records: region = record["region"] region_totals[region] = region_totals.get(region, 0) + record["revenue"] # Freeing raw data after aggregation del sales_records return region_totals result = build_sales_report() print(f"Regions: {len(result)}")

The del sales_records line shows a clear memory drop – confirming that the cleanup is effective.

Note
Note

The @profile decorator requires running the script via python -m memory_profiler your_script.py to produce line-by-line output. Inside a Jupyter environment it will execute the function normally but skip the memory report. Use memory_usage() for programmatic profiling within notebooks.

Using memory_usage() Programmatically

For use inside a script without the decorator, memory_usage() returns a time series of memory readings while a function runs:

1234567891011121314
from memory_profiler import memory_usage import time def process_batch(batch_size): data = list(range(batch_size)) time.sleep(0.1) # Simulating processing time return sum(data) # Sampling memory every 10ms during the function call mem_usage = memory_usage((process_batch, (500000,)), interval=0.01) print(f"Min memory: {min(mem_usage):.1f} MiB") print(f"Max memory: {max(mem_usage):.1f} MiB") print(f"Peak delta: {max(mem_usage) - min(mem_usage):.1f} MiB")

Comparing Two Implementations

memory_profiler makes it easy to compare the memory cost of two approaches side by side:

1234567891011121314
from memory_profiler import profile @profile def approach_list(): # Building a full list of computed values return [value ** 2 for value in range(200000)] @profile def approach_generator(): # Consuming a generator without storing results return sum(value ** 2 for value in range(200000)) approach_list() approach_generator()

Running this with python -m memory_profiler shows the memory delta for each line in both functions – approach_list will show a large allocation for the list, while approach_generator stays nearly flat.

tracemalloc vs memory_profiler

question mark

What does the Increment column in memory_profiler output represent?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

Section 3. Chapter 2

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Section 3. Chapter 2
some-alt