Python tracemalloc跟踪内存分配问题
作者:Rnan-prince
这篇文章主要介绍了Python tracemalloc跟踪内存分配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
Python tracemalloc跟踪内存分配
tracemalloc 模块是一个用于对 python 已申请的内存块进行debug的工具。
它能提供以下信息:
- 定位对象分配内存的位置
- 按文件、按行统计python的内存块分配情况: 总大小、块的数量以及块平均大小。
- 对比两个内存快照的差异,以便排查内存泄漏
显示前10项
显示内存分配最多的10个文件:
import tracemalloc tracemalloc.start() # --- 业务代码 start --- n = 10000000 s = 0 for i in range(1, n): s *= i # --- 业务代码 end --- snapshot = tracemalloc.take_snapshot() # 内存摄像 top_stats = snapshot.statistics('lineno') # 内存占用数据获取 print('[Top 10]') for stat in top_stats[:10]: # 打印占用内存最大的10个子进程 print(stat) # [Top 10] # D:/MyPython/tracemalloc/demo.py:5: size=576 B, count=1, average=576 B # D:/MyPython/tracemalloc/demo.py:7: size=28 B, count=1, average=28 B
TOP1:代码第五行占用内存大小576B
计算差异
获取两个快照并显示差异:
import tracemalloc tracemalloc.start() snapshot0 = tracemalloc.take_snapshot() # 第一张快照 # --- 业务代码 start --- n = 10000000 s = 0 for i in range(1, n): s *= i # --- 业务代码 end --- snapshot1 = tracemalloc.take_snapshot() # 第二张快照 top_stats = snapshot1.compare_to(snapshot0, 'lineno') # 快照对比 print('[Top 10 differences]') for stat in top_stats[:10]: print(stat) # [Top 10 differences] # D:/MyPython/tracemalloc/demo.py:27: size=576 B (+576 B), count=1 (+1), average=576 B # D:\Program Files\anaconda3\lib\tracemalloc.py:397: size=88 B (+88 B), count=2 (+2), average=44 B # D:\Program Files\anaconda3\lib\tracemalloc.py:534: size=48 B (+48 B), count=1 (+1), average=48 B # D:\Program Files\anaconda3\lib\tracemalloc.py:291: size=40 B (+40 B), count=1 (+1), average=40 B # D:/MyPython/tracemalloc/demo.py:31: size=28 B (+28 B), count=1 (+1), average=28 B
TOP1:代码第27行占用内存大小增加了576B
tracemalloc分析内存使用情况与泄露
概述
python内存管理是通过引用计数执行的,如果指向某个对象的引用全部过期,那么受引用的对象就可以从内存中清除,从而给其他数据腾出空间。
理论上讲,python开发不用担心程序如何分配和释放内存,因为python系统本身以及Cpython运行环境会自动处理这些问题。
但实际情况程序会因为没有及时释放不再需要引用的数据耗尽内存。下面通过一些方法来看下内存使用情况。
查看gc引用对象总数
下面是被测试代码,这个代码可以创建对象,在gc中产生引用对象。
import os class MyObject: def __init__(self): self.data = os.urandom(100) def get_data(): values = [] for _ in range(100): obj = MyObject() values.append(obj) return values def run(): deep_values = [] for _ in range(100): deep_values.append(get_data()) return
下面的代码用来输出当前gc引用对象的数量
import gc # 获取运行前gc引用对象数量 found_objects = gc.get_objects() print('Before:', len(found_objects)) # 导入待测试模块 import waste_memory # 运行待测试代码的函数 hold_reference = waste_memory.run() # 获取运行代码后gc引用对象数量 found_objects = gc.get_objects() print('After: ', len(found_objects)) for obj in found_objects[:5]: print(repr(obj)[:100]) print('...')
运行上面的代码,下面是gc引用的对象总数。
Before: 28834 After: 28923
tracemalloc查看内存分配情况
1.查看内存分配情况
上面只输出了gc的总数,对于分析内存分配情况没有太多的指导意义,tracemalloc模块能够追溯到分配它的位置,因此我们可以在之前模块前后对内存使用情况做个快照,分析两个快照之间的区别。
下面是被测试代码
import tracemalloc tracemalloc.start(10) # Set stack depth time1 = tracemalloc.take_snapshot() # Before snapshot import waste_memory x = waste_memory.run() # Usage to debug time2 = tracemalloc.take_snapshot() # After snapshot stats = time2.compare_to(time1, 'lineno') # Compare snapshots for stat in stats[:3]: print(stat)
运行上面的代码,从结果中可以看出,每一条记录都有size与count指标,用来表示这行代码所分配的对象占用多少内存,以及对象的数量。通过对比就能发现占用内存较多的对象是由那几行代码分配的。
/waste_memory.py:11: size=5120 B (+5120 B), count=80 (+80), average=64 B /waste_memory.py:14: size=4424 B (+4424 B), count=79 (+79), average=56 B /waste_memory.py:9: size=1704 B (+1704 B), count=8 (+8), average=213 B
2.查看栈信息
tracemalloc还可以打印栈的追踪信息,下面把程序中分配内存最多的那行代码所对应的栈追踪信息打印出来,看看程序是沿着哪条路径触发这行代码的。
import tracemalloc tracemalloc.start(10) time1 = tracemalloc.take_snapshot() import waste_memory x = waste_memory.run() time2 = tracemalloc.take_snapshot() stats = time2.compare_to(time1, 'traceback') top = stats[0] print('Biggest offender is:') # 打印栈信息 print('\n'.join(top.traceback.format()))
运行上面的代码
Biggest offender is: File "/with_trace.py", line 14 x = waste_memory.run() File "/waste_memory.py", line 23 deep_values.append(get_data()) File "/waste_memory.py", line 16 obj = MyObject() File "/waste_memory.py", line 11 self.data = os.urandom(100)
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。