使用memory_profiler分析Python内存管理时遇到的疑问
嘿,我来帮你拆解这个内存分析的问题~先把你的代码、执行命令和结果整理清楚,再针对这类场景的常见疑问做解答:
你的代码与分析信息
代码示例
import copy import memory_profiler @profile def function(): x = list(range(1000000)) # allocate a big list y = copy.deepcopy(x) del x return y if __name__ == "__main__": function()
执行命令
$ python -m memory_profiler memory_profiler_demo.py
部分分析结果
Filename: memory_profiler_demo.py Line # Mem usage Increment Line Contents =============================================== 4 30.074 MiB 30.074 MiB @profile 5 def function(): 6 61.441 Mi...
核心疑问解答(结合这类场景的常见困惑)
我猜你大概率是疑惑:明明执行了del x删除了大列表,为什么内存占用没有明显下降?或者深拷贝后的内存变化不符合预期?下面逐个解释:
1. del x到底做了什么?
del x不是直接释放内存,它只是移除了变量名x对那个大列表对象的引用。Python的垃圾回收(GC)基于引用计数机制:当一个对象的引用计数降到0时,GC才会回收这个对象并释放其占用的内存。
但这里有个细节:如果此时还有其他隐藏引用(比如内存分析工具本身持有临时引用,或者函数栈里的残留引用),那这个列表对象的引用计数不会立刻降到0,自然不会被回收。
2. 深拷贝的内存增长逻辑
从你的结果看,第6行创建x后,内存从30.074MiB涨到61.441MiB,这完全合理:一个包含1000000个元素的列表,在64位系统下大概占用30MB左右(列表存储的是元素指针,每个指针8字节,加上列表本身的结构开销)。执行copy.deepcopy(x)时,内存会再涨差不多30MB——因为深拷贝会创建一个完全独立的新列表,所有元素都是原列表元素的拷贝(这里是int,虽然小整数是共享的,但列表本身是新的)。
3. 为什么del x后内存没下降?
即使del x让列表的引用计数降到了0,CPython也可能不会立刻把内存还给操作系统——它会把释放的内存保留在自己的内存池中,供后续新对象分配使用。另外,memory_profiler是按行采样内存的,可能在del x那一行的采样时刻,GC还没来得及触发回收。
如果你想看到内存立刻下降,可以手动触发GC试试:
import copy import memory_profiler import gc @profile def function(): x = list(range(1000000)) # allocate a big list y = copy.deepcopy(x) del x gc.collect() # 手动触发垃圾回收 return y if __name__ == "__main__": function()
这时候再看del x之后的内存变化,应该会明显下降。
4. 函数返回后的内存变化
当函数执行完毕返回y,如果调用者没有把y赋值给任何变量(就像你的代码里那样),那么y的引用计数也会降到0,GC会回收y对应的列表。但memory_profiler默认只跟踪函数内部的内存变化,所以函数返回后的内存释放不会显示在报告里。
内容的提问来源于stack exchange,提问作者Roushan

