You need to enable JavaScript to run this app. Python解释器是否会将内存归还给操作系统?memory_profiler实验的相关疑问
优惠活动
大模型
产品
解决方案
定价
更多

Python解释器是否会将内存归还给操作系统?memory_profiler实验的相关疑问

Python解释器的内存归还机制详解

两个最常见的认知误区

  • 误区一:“Python会把所有从操作系统申请的内存攥在手里,永远不会归还”。这是对Python内存管理的过度简化,实际是分层设计的,不同类型的内存块回收路径完全不同。
  • 误区二:把“Python把已销毁对象的内存回收到自身内部缓存”等同于“把内存归还操作系统”。这是两个完全独立的过程,也是你通过mprof曲线看到内存下降时,最容易产生的误判。

分层的内存回收逻辑

Python的内存管理体系分为两层,两层的回收行为差异极大:

  • pymalloc中小对象缓存层
    Python内置的pymalloc分配器,会从操作系统批量申请固定大小(256KB)的内存块(称作arena),用来管理所有小于512字节的对象内存。这部分内存一旦从OS申请过来,绝大多数时候不会主动归还——哪怕块里已经没有正在使用的对象,也会留在Python内部的空闲块列表里,等着给后续新分配的同大小对象复用,核心目的是减少频繁调用系统内存申请接口的开销。
    只有当整个arena的所有空间都处于完全空闲的状态、没有任何存活对象占用时,pymalloc才会把这块arena交回给底层的系统malloc。至于malloc会不会真的把这部分内存还给操作系统,还要看glibc等标准C库的自身策略,默认也会保留一部分空闲堆内存,不会马上触发系统释放调用。
  • 大对象直接分配路径
    当申请的单个内存块超过pymalloc的管理阈值(默认512KB)时,pymalloc不会处理这类请求,会直接调用系统malloc向操作系统申请内存。这部分内存不会进入Python的内部缓存:当对应对象被销毁、引用计数归零时,内存会直接交回给malloc,在满足释放条件(比如空闲块位于堆顶端、合并后达到munmap的触发阈值)时,会直接通过系统调用把内存页归还操作系统,这时候你观测到的进程常驻内存(RSS)下降,才是真正的内存归还。

你的测试代码为什么会产生“内存被释放”的错觉

拆解下这段测试代码的实际运行流程,就能明白你看到的曲线变化本质:

  1. func1启动,先创建自身作用域里包含1e6个元素的列表,内存从38.3MiB涨到77.0MiB,这部分内存完全由pymalloc的arena管理。
  2. 调用func2,在func2的作用域里创建同样大小的1e6元素列表,由于这是第二个独立的存活对象,内存会再上涨约38MiB,达到115MiB左右的峰值,并保持10秒(因为func2里的time.sleep(10))。
  3. func2执行完退出,它作用域里的列表引用计数归零,对应的内存被pymalloc回收,回到Python的内部空闲块列表。这时候mprof的时间曲线会画出一个从115MiB掉回77MiB的下坡——你看到的“内存释放”就是这个过程,但这部分内存根本没有还给操作系统:它只是从“被对象占用”的状态,变成“Python攥在手里、留给后续分配使用的空闲缓存”。而逐行监控输出里,func2()那一行的增量只有0.3MiB,本质是因为监控是在func2完全执行完之后才读取的RSS值,这时候func2创建的列表已经被销毁、内存回到Python缓存了。

memory_profiler的测量逻辑误区

你之前认为这个工具“只测运行代码的资源使用、不含解释器本身占用”,这也是常见误解:
memory_profiler的逐行监控、时间曲线读取的是进程的整体常驻内存(RSS)值,这个值会把所有属于当前进程的物理内存页全部算进去——不管是解释器本身的基础运行内存、你代码创建的对象占用的内存、还是Python回收后攥在手里的空闲缓存内存,都包含在测量结果里。
你看到的逐行增量,只是前后两次测量的RSS差值,并没有区分“哪部分是实际被代码占用的、哪部分是缓存占的”,所以才会出现func2明明创建了和func1一样大的列表,但测出来的增量几乎可以忽略的情况。

什么情况下才会观测到真实的内存归还

只有满足以下场景,才会出现Python把内存真正交回操作系统的情况:

  • 你分配了远大于管理阈值的大对象(比如几个GB的数组、数百万个长字符串组成的列表),这类对象走的是直接malloc的路径,销毁时会触发munmap完成内存归还。
  • 如果是pymalloc管理的小对象,哪怕你把所有存活对象都销毁,只要Python的空闲arena还没达到内部的清理阈值,内存就会一直留在进程的地址空间里,不会还给OS。
  • 很多时候监控曲线里的RSS下降,根本不是Python主动释放内存,是操作系统的虚拟内存机制把长期不访问的内存页换到了swap分区——这部分内存本质上还是属于Python进程的,等后续程序访问到对应位置时还会被换回来,和内存归还没有关系。

内容的提问来源于stack exchange,提问作者Альберт Александров

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 18:45:05