导读:本期聚焦于坚哥创作的《如何用 Python tracemalloc 快速定位内存泄漏和内存占用过高问题》,敬请观看详情。在 Python 程序运行过程中,内存占用突然上涨或者缓慢泄漏是很多开发者都会遇到的麻烦。明明代码逻辑看起来没问题,但进程占用的内存就是降不下来。tracemalloc 是 Python 标准库中专门用于跟踪内存分配的工具,不需要安装第三方包就能用。它可以帮助我们清楚地看到每一块内存是在哪一行代码、哪一个函数中分配出来的,从而快速找到占用内存最多的地方。本文会介绍 tracemalloc 的基本用法,包括如何开启跟踪、获取内存快照、对比不同时刻的快照,以及怎样通过输出结果定位到具体的代码位置,帮你在排查内存问题时少走弯路。

Python 的内存管理通常由解释器自动完成,但在长时间运行的服务、缓存系统或批量数据处理任务中,某些对象可能被意外保留,导致内存占用持续上升。标准库中的 tracemalloc 模块可以记录 Python 层内存分配的调用栈,帮助开发者把内存增长定位到具体代码位置。它从 Python 3.4 起成为标准库的一部分,无需安装第三方依赖,使用门槛较低。

如何用 Python tracemalloc 快速定位内存泄漏和内存占用过高问题

tracemalloc 的跟踪机制与启动方式

tracemalloc 的核心工作方式是在 Python 分配内存时记录当前调用栈。调用 tracemalloc.start() 后,解释器会保存每一次内存分配对应的栈帧,开发者可以指定保存的栈帧深度。栈帧越深,得到的调用链越完整,但也会带来更多的记录开销和内存消耗。因此在实际使用中,通常根据排查需要设置一个合理的深度,例如 25 层已经能够覆盖大多数业务调用层级。

为了保证覆盖到全部内存行为,建议在程序入口的最早位置开启跟踪。如果跟踪启动得太晚,启动之前的分配记录将无法被捕获,可能导致内存泄漏的源头被遗漏。对于 Web 服务或后台任务,可以在进程启动时开启,而不是等到某个请求处理函数内部再开启。

import tracemalloc

# 开启内存跟踪,保存 25 层调用栈
tracemalloc.start(25)

def build_large_list():
    # 模拟创建一个较大的列表对象
    return [item for item in range(100000)]

cache = build_large_list()

上面的代码在程序入口处启动了 tracemalloc,并保留 25 层调用栈。build_large_list() 创建了一个包含十万个整数的列表,后续可以通过快照观察这段分配占用的内存大小。需要说明的是,tracemalloc 跟踪的是 Python 层面的分配,整数、列表、字符串等对象都会进入统计范围。

获取内存快照并理解统计输出

开启跟踪后,可以在任意时刻调用 tracemalloc.get_snapshot() 获取当前内存快照。快照中保存了所有尚未释放的 Python 对象分配信息,包括分配位置和占用大小。拿到快照后,通常需要调用 snapshot.statistics() 进行聚合,以便快速找到占用最高的代码位置。

import tracemalloc

tracemalloc.start()
# 执行业务逻辑,生成一组数据
data = [value * 2 for value in range(50000)]
snapshot = tracemalloc.get_snapshot()
# 按代码行号统计,并按内存占用降序排列
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
    print(stat)

statistics() 方法支持不同的统计维度。lineno 按文件行号聚合,适合快速定位到具体某一行;filename 按文件聚合,适合从整体上观察哪个模块占用最高;traceback 则保留完整调用栈,适合需要深入分析调用链的场景。每条统计记录中的 size 表示该位置当前占用的总字节数,count 表示分配次数。需要注意的是,这里的 count 并非当前存活对象的数量,而是累计分配次数减去释放次数的结果。

统计维度适用场景
lineno快速查看某一行代码占用了多少内存
filename查看哪个文件整体内存占用最高
traceback需要完整调用链时使用

同一个程序在不同统计维度下会有不同的表现。例如,一个工具函数被多个模块调用,按 lineno 统计可能分散到不同调用点,而按 filename 统计则更能体现该函数所在文件的整体贡献。因此,在排查时可以先使用 filenamelineno 找到大致范围,再使用 traceback 查看完整调用关系。

通过快照对比定位内存增长

单次快照只能反映某一时刻的内存分布,无法区分哪些内存是历史遗留的,哪些是刚刚新增的。如果程序在运行一段时间后内存明显升高,更好的做法是在两个时间点分别获取快照,然后调用 compare_to() 得到差异统计。这样可以直接看到两次快照之间哪些代码位置新分配了最多内存。

import tracemalloc

tracemalloc.start()
first_snapshot = tracemalloc.get_snapshot()

# 模拟运行一段时间后新增缓存数据
cache = []
for _ in range(5):
    cache.append(' ' * 10 ** 6)

second_snapshot = tracemalloc.get_snapshot()
# 对比两次快照,输出增长最多的 5 条记录
diff_stats = second_snapshot.compare_to(first_snapshot, 'lineno')
for stat in diff_stats[:5]:
    print(stat)

在上述示例中,程序首先记录了一次初始快照,随后向 cache 列表中追加了五个较大的字符串。第二次快照与第一次快照对比后,compare_to() 会按照内存增长量降序排列差异记录。这样,开发者可以迅速看到 cache.append() 所在行成为内存增长的主要来源。如果这种增长在业务上是不合理的,就可以进一步检查是否存在缓存未清理、全局变量持续累积或其他保留引用的问题。

对比快照的方法非常适合周期性任务和请求处理场景。例如,可以在每次请求处理前获取一次快照,在处理完成后再获取一次快照,如果发现某次接口调用后内存明显上涨且未回落,就能通过差异记录找到对应代码行。

实际排查建议与注意事项

在真实项目中,不建议长期开启 tracemalloc,因为它会显著增加内存和 CPU 开销。更好的方式是在需要排查时临时开启,或者仅在开发环境、预发布环境中进行采样。

Pythontracemalloc内存泄漏修改时间:2026-07-30 22:42:22

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。