memory-tools¶
A set of simple yet effective tools to troubleshoot memory leaks.
When debugging memory issues in Python 2.6, the author had tried memory_profiler and heapy, unfortunately neither worked. And so memory-tools was born with the goal of being simple - it should always work, yet effective - it is good at helping you find memory leaks.
Quick Start Tutorial¶
Installation¶
$ pip install memory-tools
Show Memory Usage / Delta¶
Use the show-mem command to show system or process memory usage. When paired with watch, this becomes even more useful.
Show system memory:
$ show-mem
Commit Mem (MB): 27,852.80 total 17,278.42 used
Physical Mem (MB): 16,384.00 total 13,128.05 used
Re-run to show delta from last run:
$ show-mem
Commit Mem (MB): 27,852.80 total 17,888.59 used (delta: 310.15)
Physical Mem (MB): 16,384.00 total 13,126.40 used (delta: -1.65)
Show memory for process:
$ show-mem -p python
1 process matching "python":
PID 26143 (MB): 4.79 rss 1.23 private
$ show-mem -p 26143
PID 26143 (MB): 4.80 rss 1.24 private
Watch system/process memory using watch:
$ watch show-mem -s -p python
Commit Mem (MB): 27,852.80 total 17,888.59 used (delta: 310.15)
Physical Mem (MB): 16,384.00 total 13,126.40 used (delta: -1.65)
2 processes matching "python" (showing 1st & last):
PID 26143 (MB): 40.79 rss 30.23 private
PID 24118 (MB): 4.79 rss 1.23 private
Summarize / Save GC Objects¶
After running your program, view summary of gc.get_objects():
from memorytools import summarize_objects
summarize_objects()
And here is a sample output:
Objects count 3,790
Objects size 833,344
Size Count Type
476,864 296 <type 'dict'>
76,320 954 <type 'wrapper_descriptor'>
64,920 541 <type 'function'>
...
Count Size Type
954 76,320 <type 'wrapper_descriptor'>
541 64,920 <type 'function'>
515 37,080 <type 'builtin_function_or_method'>
...
Save all objects (along with the above summary) to a file:
from memorytools import save_objects
save_objects()
# Output: Wrote 3887 objects to /var/tmp/objects-45271 (882040 bytes)
Looping / Stress Testing¶
Use the loop command to run a command, module:method, or code in a forever loop to perform stress testing, which is useful in finding memory leaks. The command/code should, of course, act against a long running server for this to be useful.
Run a script in a loop:
$ loop show-mem 1
Physical Mem (MB): 16,384.00 total 9,415.50 used (delta: -190.67)
Physical Mem (MB): 16,384.00 total 9,415.27 used (delta: -0.23)
Physical Mem (MB): 16,384.00 total 9,415.85 used (delta: 0.58)
^C [ User CTRL-C here as it loops forever by default ]
Looped 3 times in 2.80 secs
Run a module:method in a loop - count of 10:
$ loop memorytools:summarize_objects 10 -c 10
# Results from summarize_objects() every 10 seconds
Looped 10 times in 100 secs
Run adhoc code in a loop - count of 2 and concurrency of 3:
$ loop 'print("Hello World!")' 0.1 -c 2 -cc 3
Hello World!
... 5 more times
Looped 2 times in 0.21 secs with concurrency of 3 (6 runs, 0.10 secs per loop, 0.03 secs per run)
Log Stack / Start Debugger on Signal¶
If you need to get a stacktrace of a running process, or start the debugger in specific situations to look at memory footprint, then a signal handler could help:
from memorytools import add_debug_handler
add_debug_handler(start_debugger_password='test') # remove start_debugger_password to skip rpdb2 debugger
The above will add a handler to SIGUSR2 that will log a stacktrace on trigger and also start the rpdb2 debugger.