KernelRanch
返回文章列表

Python 进程内存持续上升:用 tracemalloc 定位仍存活的分配

2026-11-13 #python#memory#profiling

相同批次反复处理后,进程占用一直变大。先在安静点抓两张 Python 分配快照,按行号看哪些分配一直存在。再清理可疑持有者,第三张快照用来验证这些对象是否消失。

1. 只比较一个已经知道的分配位置

保存为 memory_lab.py,运行 python memory_lab.py。

import gc
import tracemalloc

def fill(cache):
    for key in range(40):
        cache[key] = bytearray(128 * 1024)  # tracked allocation

def measured_growth(before, after, line):
    return sum(
        stat.size_diff for stat in after.compare_to(before, "lineno")
        if stat.traceback[0].filename == __file__ and stat.traceback[0].lineno == line
    )

line = fill.__code__.co_firstlineno + 2
cache = {}
tracemalloc.start()
try:
    before = tracemalloc.take_snapshot()
    fill(cache)
    # This temporary allocation has ended before the snapshot.
    temporary = bytearray(1024 * 1024)
    del temporary
    retained = tracemalloc.take_snapshot()
    growth = measured_growth(before, retained, line)
    assert growth >= 5 * 1024 * 1024
    print("retained payload: at least 5 MiB")

    cache.clear()
    gc.collect()
    after = tracemalloc.take_snapshot()
    assert measured_growth(before, after, line) == 0
    print("after clearing cache: allocation line back to baseline")
finally:
    tracemalloc.stop()

本机实测输出如下。

retained payload: at least 5 MiB
after clearing cache: allocation line back to baseline

2. 在真实负载里缩小范围

真实排查时先按 lineno 排序看正增长,再按 traceback 聚合追到调用入口。对照快照应在相近负载、相同并发和同一业务阶段采集。例如每批队列排空后采样,避免把不同数量的进行中请求误判为泄漏。

观察结果下一步
固定分配行在多批次后持续净增长检查缓存、队列或全局对象如何保留引用
清理引用后快照下降,进程 RSS 仍高检查分配器复用及操作系统内存指标
RSS 上升,Python 快照几乎不变调查本机扩展、映射文件或其他分配来源

tracemalloc 给出分配位置。要确认谁持有引用,需要结合缓存条目数、对象生命周期和应用代码。启用追踪有开销,可以先在复现场景启用,提前开始采集以免漏掉基线。

小结

用同一批次边界采样,找出仍存活的分配,再通过释放引用后的快照验证原因。

数据来源与检索时间

检索时间 2026-11-13。

来源URL对应内容
Python tracemallochttps://docs.python.org/3.11/library/tracemalloc.html快照比较、行号和追踪范围

核验说明

  • Python 3.11.9 验证保留的 bytearray 和清理后的快照。实验未用 RSS 推算 Python 对象大小。