Python tracemalloc跟踪内存分配问题

 更新时间:2023年11月09日 09:18:44   作者: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)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 一文搞懂Python的hasattr()、getattr()、setattr() 函数用法

    一文搞懂Python的hasattr()、getattr()、setattr() 函数用法

    python中的getattr()、setattr()、hasattr()函数均是对类属性或方法的操作,其中getattr()用于获取类或实例中指定方法获取属性的值,setattr()用于设置类或实例中属性或方法,hasattr()用于判断类或实例中是否存在指定的属性或方法,本文通过例子给大家详解,一起看看吧
    2022-04-04
  • Starship定制shell提示符实现信息自由

    Starship定制shell提示符实现信息自由

    这篇文章主要介绍了Starship定制shell提示符的实现,让你需要的所有信息触手可及,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • Python开发工具PyCharm的下载与安装步骤图文教程

    Python开发工具PyCharm的下载与安装步骤图文教程

    这篇文章主要为大家介绍了Python开发工具PyCharm的下载与安装步骤图文教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • 解决python logging遇到的坑 日志重复打印问题

    解决python logging遇到的坑 日志重复打印问题

    这篇文章主要介绍了解决python logging遇到的坑 日志重复打印问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • Python利用Pydub实现自动分割音频

    Python利用Pydub实现自动分割音频

    pydub是一个轻量级的音频处理库,安装方便,使用简单。而且pydub提供了丰富的音频处理功能,包括切割、合并、转换等。本文将利用Pydub实现自动分割音频功能,感兴趣的可以了解一下
    2023-05-05
  • Python科学画图代码分享

    Python科学画图代码分享

    这篇文章主要介绍了Python科学画图代码分享,涉及matplotlib库的简单介绍,分享了matplotlib绘图库书籍的下载地址,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Python大数据之网络爬虫的post请求、get请求区别实例分析

    Python大数据之网络爬虫的post请求、get请求区别实例分析

    这篇文章主要介绍了Python大数据之网络爬虫的post请求、get请求区别,结合具体实例形式分析了Python网页爬虫post请求与get请求相关使用技巧,需要的朋友可以参考下
    2019-11-11
  • Python 中 sorted 如何自定义比较逻辑

    Python 中 sorted 如何自定义比较逻辑

    这篇文章主要介绍了Python中sorted如何自定义比较逻辑,帮助大家更好的理解和学习使用python,感兴趣的朋友可以了解下
    2021-02-02
  • Python遇到UnicodeEncodeError错误的解决方案

    Python遇到UnicodeEncodeError错误的解决方案

    在使用Python处理从不同网页抓取的文本时,经常会遇到UnicodeEncodeError错误,这通常是因为Python默认使用ASCII编码,而当遇到超出ASCII编码范围(0 - 127)的字符时,就会抛出该错误,所以本文给大家介绍了一些Python遇到UnicodeEncodeError错误的解决方案
    2025-06-06
  • django执行原始查询sql,并返回Dict字典例子

    django执行原始查询sql,并返回Dict字典例子

    这篇文章主要介绍了django执行原始查询sql,并返回Dict字典例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04

最新评论