You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何准确测量Python函数运行过程消耗的总内存?

问题原因

两个现有方案失效的核心原因如下:

  • 基于psutil读取RSS的方案统计的是操作系统视角下进程实际占用的物理内存差值,完全受Python内存池机制影响:Python通过pymalloc实现了多层内存池,对象销毁后内存不会立刻归还给操作系统,而是留在池中供后续复用。第一次执行函数时申请的大块内存,在del x后只是回到了Python内部内存池,没有还给OS,后续调用函数时直接从池中取内存,不会触发新的物理内存申请,因此RSS差值为0。同时这个方案完全统计不到中途申请又释放的内存,根本无法反映执行流程的总内存分配量。
  • tracemalloc并非只能统计当前内存和峰值内存,多数人是没有用对它的能力:它本身可以跟踪Python层所有内存分配事件,完全可以实现类似Julia @btime的累计总内存统计效果。
推荐解决方案

方案1:原生tracemalloc实现(无额外依赖,统计Python层分配)

适用于纯Python代码的内存统计,准确性高,不受内存池影响,多次调用结果稳定。Python3.12及以上版本可直接使用如下装饰器:

import tracemalloc
from functools import wraps

def profile_total_memory(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 兼容全局已启动tracemalloc的场景
        need_stop = not tracemalloc.is_tracing()
        if need_stop:
            tracemalloc.start()
        
        # 重置历史跟踪记录
        tracemalloc.clear_traces()
        total_alloc = 0

        # 注册分配回调,每次内存分配就累加大小,和后续是否释放无关
        def count_alloc(size, _):
            nonlocal total_alloc
            total_alloc += size
        tracemalloc.add_trace_callback(count_alloc)

        try:
            res = func(*args, **kwargs)
        finally:
            tracemalloc.remove_trace_callback(count_alloc)
            if need_stop:
                tracemalloc.stop()

        # 单位格式化
        def fmt(size):
            for u in ["B", "KB", "MB", "GB"]:
                if size < 1024 or u == "GB":
                    return f"{size:.2f} {u}"
                size /= 1024

        print(f"[{func.__name__}] 累计总分配内存: {fmt(total_alloc)}")
        cur_mem, peak_mem = tracemalloc.get_traced_memory()
        print(f"[{func.__name__}] 执行后存活内存: {fmt(cur_mem)}, 峰值内存: {fmt(peak_mem)}")
        return res
    return wrapper

使用时直接装饰目标函数即可,多次调用统计结果一致:

@profile_total_memory
def func():
    x = [1] * (10 ** 7)
    y = [2] * (4 * 10 ** 8)
    del x
    return y

# 连续调用两次
func()
func()

这个方案统计的是函数执行期间所有Python对象的内存申请总和,不管内存中途是否释放、是否走内存池复用,完全对应@btime的总内存统计逻辑。

如果你使用Python3.12以下版本,tracemalloc未开放分配回调接口,可以通过跟踪所有内存块的分配地址、记录新增分配大小的方式实现类似逻辑,只是代码复杂度稍高。

方案2:全链路内存跟踪(覆盖C扩展分配)

如果函数中调用了numpy、pandas这类基于C实现的第三方库,C代码直接通过系统malloc申请的内存无法被tracemalloc捕获,此时可以使用挂钩libc内存分配接口的工具做统计。这类工具直接在操作系统层面拦截所有malloc/free调用,不会受Python内存池、C层内存池的影响,可以统计到函数执行期间所有的内存申请总量,包括中途释放的部分,统计结果最准确。

注意事项

不要用RSS差值做精确内存统计:RSS是进程持有的物理页计数,受操作系统页回收策略、运行时内存池、GC时机的影响极大,只能做粗略的进程内存占用观测,完全无法用来统计单段代码的内存分配总量。

内容的提问来源于stack exchange,提问作者nietoperz21

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:21:26