Python中中间值内存处理机制的疑问及实测分析
Python中中间值内存处理机制的疑问及实测分析
最近我在研究Python里不同序列构建方式的内存占用时,遇到了一个和直觉不太一致的情况,想和大家聊聊。
比如,当我需要从两个列表构建一个新对象时,直觉上觉得先拼接列表再迭代,会因为生成了临时的拼接列表而占用更多内存;但如果用itertools.chain来逐个生成元素,应该能避免这个临时列表的内存开销。但实测下来,两者的内存占用居然完全一样,这就让我有点懵了——难道是我的测试方法有问题?
我写了一段测试代码来验证:
from itertools import pairwise, chain from random import choice from string import ascii_lowercase import tracemalloc def measure_allocation(): s = [choice(ascii_lowercase) for _ in range(1_000_000)] tracemalloc.start() # 第一种方式:列表拼接后转元组 res1 = tuple(['^']+s) snapshot1 = tracemalloc.take_snapshot() top_stats = snapshot1.statistics('lineno') for stat in top_stats[:10]: print(stat) tracemalloc.stop() print('*'*80) tracemalloc.start() # 第二种方式:用chain串联后转元组 res2 = tuple(chain(['^'],s)) snapshot2 = tracemalloc.take_snapshot() top_stats = snapshot2.statistics('lineno') for stat in top_stats[:10]: print(stat) tracemalloc.stop() if __name__ == "__main__": import sys print(sys.version) print('*'*80) measure_allocation()
运行后的结果是:
3.12.6 (main, Sep 6 2024, 19:03:47) [GCC 13.3.0] ******************************************************************************** /tmp/tmp.MbQI2VsYCZ/foo.py:13: size=7813 KiB, count=1, average=7813 KiB ******************************************************************************** /tmp/tmp.MbQI2VsYCZ/foo.py:24: size=7813 KiB, count=1, average=7813 KiB
按我原来的理解,第一种方式(res1 = tuple(['^']+s))的执行流程应该是这样的:
- 先计算内部的
['^'] + s,生成一个全新的临时列表,这个列表的大小和s差不多(毕竟列表扩容是按块来的) - 这时候内存里同时存在原列表
s和这个临时列表,内存占用应该是翻倍的 - 然后再迭代这个临时列表来构建元组,之后临时列表才会被垃圾回收
但实测结果显示,两种方式的内存峰值居然完全一致,看起来好像只分配了元组的内存,完全没有临时列表的开销?这就让我很疑惑,到底是我的测试哪里错了,还是Python的内存处理机制和我想的不一样?
备注:内容来源于stack exchange,提问作者Mathias Sven




