Tracemalloc#

Tracemalloc is a package that monitors memory allocation in python.

Statistics#

The tracemalloc stores memory allocation information in special Statistic objects. It has 3 fileds:

  • traceback: object that refers to the line that allocated memory.

  • size: size in bytes of the memory allocated.

  • count: the number of blocks that have been allocated. A block is a contiguous piece of memory that is allocated separately from others.


The following cell takes the random allocation record that we’ll use as an example and shows representation of the statistics object as a string.

import tracemalloc

tracemalloc.start()
ans = tracemalloc.take_snapshot().statistics('lineno')
stat_trace = ans[0]
print(stat_trace)
/usr/local/lib/python3.13/linecache.py:172: size=279 KiB, count=3002, average=95 B

The next code shows the type of the object we’re dealing with.

type(stat_trace)
tracemalloc.Statistic

Next three cells shows count, size and traceback attibutes of the statistic.

stat_trace.count
3002
stat_trace.size
285727
stat_trace.traceback
<Traceback (<Frame filename='/usr/local/lib/python3.13/linecache.py' lineno=172>,)>

Peak#

The tracemalloc.get_traced_memory function returns the current amount of used memory and the memory usage peak. For more details, check the tracemalloc.get_traced_memory section of the official documentation.


The following example creates a script that calls some_function that just allocates memory for res = [0] and then exits. This causes the garbage collector to immediately take all objects allocated by some_function.

It then calls tracemalloc.get_traced_memory and prints the outputs: current and peak memory usage.

%%writefile /tmp/tracemalloc_files.py 
import tracemalloc

def some_function():
    res = [0]

tracemalloc.start()
some_function()

curr, peak = tracemalloc.get_traced_memory()
print(curr, peak)
Overwriting /tmp/tracemalloc_files.py
!python3 /tmp/tracemalloc_files.py
0 8

The result is that the currently allocated memory is empty after calling tracemalloc.start(). However, when the function was executed, the memory consumption peaked at 8 bytes.

Monitoring scope#

This section explains which actions can be added to the tracemalloc results and how to work with them.

Operations that was performed before the tracemalloc.start() call obviously are not included in the tracemalloc outputs. Therefore, all monitoring begins with the tracemalloc.start() call.


The code represented in the following cell attempts to create a two lists: one before the tracemalloc.start() call and one after.

%%writefile /tmp/tracemalloc_scope.py
import tracemalloc

lst1 = [20]

tracemalloc.start()

lst2 = [30, 20]

ans = tracemalloc.take_snapshot().statistics("lineno")
for v in ans:
    print(v)
Overwriting /tmp/tracemalloc_scope.py
!python3 /tmp/tracemalloc_scope.py
/tmp/tracemalloc_scope.py:7: size=16 B, count=1, average=16 B

There is only one allocation mentioned in traces, and it belongs to the lst2 = [30, 20] assignment.

Restart#

Double call of the tracemalloc.start() does nothing.

It doesn’t restart tracemalloc with reset of all traces, even through that might seem intuitive.


The following cell shows the code that declares lists both after the corresponding tracemalloc.start() call.

%%writefile /tmp/tracemalloc_scope.py
import tracemalloc

tracemalloc.start()
lst1 = [20]

tracemalloc.start()
lst2 = [30, 20]

ans = tracemalloc.take_snapshot().statistics("lineno")
for v in ans:
    print(v)
Overwriting /tmp/tracemalloc_scope.py
!python3 /tmp/tracemalloc_scope.py
/tmp/tracemalloc_scope.py:4: size=408 B, count=2, average=204 B
/tmp/tracemalloc_scope.py:7: size=16 B, count=1, average=16 B

The resutls include allocations associated with creating lists. Therefore, the second tracemalloc.start() call does not clear the accumulated traces.

Clear traces#

If, at any point, you need to ignore traces that have appeared since the tracemalloc.start call, you should use tracemalloc.clear_traces().


The following cell causes python to allocate memory before and after tracemalloc.clear_traces().

%%writefile /tmp/tracemalloc_scope.py
import tracemalloc

tracemalloc.start()
lst1 = [20]

tracemalloc.clear_traces()
lst2 = [30, 20]

ans = tracemalloc.take_snapshot().statistics("lineno")
for v in ans:
    print(v)
Overwriting /tmp/tracemalloc_scope.py
!python3 /tmp/tracemalloc_scope.py
/tmp/tracemalloc_scope.py:7: size=16 B, count=1, average=16 B

Consequently, there is only the trace corresponding to the creation of the second list.