news 2026/4/28 11:25:09

python tracemalloc

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python tracemalloc

# Python tracemalloc:一款被低估的内存追踪利器

它到底是什么

开始讲tracemalloc之前,得先聊聊Python的内存管理。很多人可能觉得Python有垃圾回收机制,内存问题就不用太操心。但这个想法其实挺危险的——垃圾回收只管“回收”那些不再使用的对象,但如果你不小心创建了大量不会销毁的对象,或者无意中持有了一些永远不会释放的引用,内存还是会悄悄涨上去。

tracemalloc是Python 3.4开始内置的一个模块,专门用来追踪内存分配情况。它不是那种花哨的调试工具,默默无闻地藏在标准库的角落里,但我敢说,熟练用好它的人并不多。

它的底层原理挺有意思——通过钩子机制拦截Python的内存分配调用。每当代码里创建新对象时(比如listdict,甚至一个简单的整数),tracemalloc都能记录下来。这有点像给内存装了个行车记录仪,每一笔内存分配的“案发时间”和“案发地点”都被保存下来。

它能做什么

有个实际例子能说明问题。之前帮朋友排查一个FastAPI服务的内存泄漏,服务跑个两三天内存就涨到3G多。常规手段基本都试过了——gc.get_objects()看对象数量、objgraph画引用关系图、甚至用sys.getsizeof手工估算。但这些方法要么太粗糙只能看个大概,要么太复杂查起来很累。

tracemalloc这时候就显身手了。它能直接告诉你:

  • 哪行代码分配了最多内存
  • 哪些对象占据了大部分内存空间
  • 对比两个时间点的内存快照,找出新增的内存分配

更有用的是,它能对比“快照”。就像给内存拍两张照片,一张是服务刚启动时,一张是跑了24小时后,然后直接比对差异,找出新增的内存都在哪儿分配的。

怎么使用

使用起来相当直接,不需要装额外的东西。直接导入就行:

importtracemalloc# 启动追踪tracemalloc.start()# 跑你的业务代码...do_something()# 拍个快照snapshot=tracemalloc.take_snapshot()

但我发现很多人不知道的一个技巧——start()其实可以设参数,比如tracemalloc.start(25)能设置追踪的栈帧深度。默认是1,意味着只能看到当前函数所在的文件行。设到25才能看到完整的调用栈,这对分析复杂的库调用特别有用。

对比快照的方式是我最常用的:

# 先在某个时间点拍个快照snapshot1=tracemalloc.take_snapshot()# 跑一段时间...process_data()# 再拍一个snapshot2=tracemalloc.take_snapshot()# 计算差异stats=snapshot2.compare_to(snapshot1,'lineno')

compare_to方法有个key_type参数可以选,像'lineno'按文件行号分组,'traceback'按完整调用栈分组,'filename'按文件名分组。'traceback'最详细但结果也最难看懂。

打印结果时,Statistics对象自带top方法和sort方法:

forstatinstats[:10]:print(stat)

输出的格式大概是这样的:

/Users/me/project/app.py:42: size=12.5 MiB (+5.2 MiB), count=230 (+87), average=55.7 KiB

意思是app.py第42行,当前占12.5MB内存,比上一次快照多了5.2MB,总共230个对象,比之前多了87个。

最佳实践

真正要高效用tracemalloc,有几个经验值得分享。

运行在独立进程中。这个坑踩过才知道——tracemalloc本身会引入额外的内存占用和性能损耗。在生产环境直接开着追踪,相当于给服务多套了一层负担。更好的做法是在独立脚本或者单独的进程里做快照。比如启动主服务时加个环境变量控制,默认不开启追踪。需要排查时才单独开一个进程做快照。

设置合理的帧深度。默认的帧深度1基本等于没用,只能看到哪个函数在分配内存。我一般设到1015,既能看到完整的调用链,又不会让追踪本身的性能开销太大。设到30以上真的会明显卡顿。

结合上下文使用。tracemalloc有个特别实用的功能——get_traced_memory(),可以看看当前分配了多少内存。但这个值在程序启动时和运行一段时间后会差很多。我常用的是搭配start()stop()来控制追踪的范围:

tracemalloc.start(15)# 只追踪这一段代码的分配result=heavy_function()snapshot=tracemalloc.take_snapshot()tracemalloc.stop()

这样只追踪关键函数,避免追踪整个运行周期的噪声数据。

利用filter_traces。有时候并不关心标准库或第三方包的内部实现,只想知道自己代码的问题。这时可以用:

snapshot=snapshot.filter_traces([tracemalloc.Filter(True,"<match>"),tracemalloc.Filter(False,"<ignore>"),])

第一个参数是inclusiveTrue表示包含匹配的,False表示排除匹配的。比如排除所有/usr/lib/python3下的文件:

snapshot=snapshot.filter_traces([tracemalloc.Filter(False,"/usr/lib/python3*")])

和同类技术对比

讲tracemalloc就绕不开和memory_profiler以及objgraph的比较。

memory_profiler的优势是能把内存使用情况画成图,而且是装饰器风格,用起来很优雅。但它有个致命缺点——对多线程支持不好,而且在追踪大型任务时,每行代码都记录内存变化,性能开销很大。拿跑一个训练模型来说,加了装饰器之后运行时间可能翻倍。

objgraph擅长画对象的引用关系图,对排查循环引用或者不释放的对象特别有用。但它的信息维度比较单一,只看引用关系,不注重分配位置。换句话说,你知道有几个对象在互相引用,却不清楚它们是从哪里冒出来的。

tracemalloc正好互补——它不关心对象之间的引用关系,只关心这些对象“出生”在哪里。所以我会这么搭配使用:

  • 先拿tracemalloc找出内存增长最明显的代码位置
  • 再用objgraph深入了解那个位置创建的对象是怎么被引用的

另外,tracemalloc还有个不容易注意到的优点——它是Python内置的。不需要pip安装任何东西,不需要担心版本兼容问题。在生产环境(只要不是极端低版本),直接import tracemalloc就能用。

要说缺点的话,就是tracemalloc的输出不够直观。memory_profiler可以一行一行地标注内存占用,而tracemalloc给出的只是统计信息。好在Python 3.6之后,tracemalloc增加了get_object_traceback()方法,能直接查特定对象在哪个位置分配的:

importtracemalloc tracemalloc.start()obj=allocate_something()tb=tracemalloc.get_object_traceback(obj)print(tb)

这个功能在调试时真的很好用——如果发现某个变量一直占用大量内存,直接查它的“出生档案”,就知道它在哪行代码创建的。

最后提一下,tracemalloc不是万能的。它没法追踪C扩展模块通过malloc直接分配的那部分内存(比如numpy数组的底层内存),那些内存只在numpy的Python对象层面被记个总账。真要查C扩展模块的内存占用,可能还得配合valgrind这样的工具。但在纯Python代码的范畴内,tracemalloc算是定位内存问题的第一块敲门砖,也是最实用的一块。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 11:24:27

5分钟快速上手PPTist:免费开源的在线PPT制作工具终极指南

5分钟快速上手PPTist&#xff1a;免费开源的在线PPT制作工具终极指南 【免费下载链接】PPTist PowerPoint-ist&#xff08;/pauəpɔintist/&#xff09;, An online presentation application that replicates most of the commonly used features of MS PowerPoint, allowing…

作者头像 李华
网站建设 2026/4/28 11:23:24

解决AI落地难:基于BuildingAI搭建AI智能体训练助手

一、场景痛点与目标 企业在落地AI自动化解决方案时&#xff0c;常常面临“技术栈碎片化、商用闭环难搭建、多工具协同低效、定制化成本高”等现实问题。自研一套完整的AI智能体系统需要整合模型服务、工作流编排、知识库管理、用户体系、支付计费等模块&#xff0c;从零开发周…

作者头像 李华
网站建设 2026/4/28 11:23:20

一招搞定!用本地device.json文件解决云服务器go-cqhttp扫码登录失败

云服务器go-cqhttp扫码登录失败的终极解决方案&#xff1a;本地device.json文件替换指南 如果你在云服务器上部署go-cqhttp时遇到扫码登录失败的问题&#xff0c;而本地环境却能正常登录&#xff0c;那么这篇文章就是为你量身定制的。我们将深入探讨这个问题的根源&#xff0c;…

作者头像 李华
网站建设 2026/4/28 11:20:21

避坑指南:手把手教你用C语言操作H264裸流,插入SEI数据不踩雷

避坑指南&#xff1a;手把手教你用C语言操作H264裸流&#xff0c;插入SEI数据不踩雷 在音视频开发领域&#xff0c;H264作为最主流的视频编码标准&#xff0c;其底层操作一直是开发者必须掌握的硬核技能。但当你需要直接操作H264裸流时&#xff0c;往往会遇到各种"坑"…

作者头像 李华
网站建设 2026/4/28 10:32:20

强力浏览器法线贴图生成器:零门槛提升3D材质质感的完整方案

强力浏览器法线贴图生成器&#xff1a;零门槛提升3D材质质感的完整方案 【免费下载链接】NormalMap-Online NormalMap Generator Online 项目地址: https://gitcode.com/gh_mirrors/no/NormalMap-Online NormalMap-Online是一款基于WebGL技术的在线法线贴图生成工具&…

作者头像 李华
网站建设 2026/4/28 10:31:50

ECS ADLN-IE1S工业主板:Alder Lake-N架构与工业级设计解析

1. ECS ADLN-IE1S工业主板深度解析在工业自动化领域&#xff0c;主板的可靠性和稳定性直接决定了整个系统的运行质量。ECS最新推出的ADLN-IE1S 3.5英寸工业主板&#xff0c;凭借其独特的硬件配置和工业级设计&#xff0c;为苛刻环境下的嵌入式应用提供了新的解决方案。这款主板…

作者头像 李华