Python列表内存字节分配问题:为何列表内存小于元素内存总和?
你猜测的「getsizeof仅返回列表结构自身的内存占用」完全正确,这就是数值看起来不符合直觉的根本原因。
你可以通过以下代码复现测试结果(原示例省略了必要的模块导入):
import sys intA = 10 A = [x for x in range(0,10)] print(sys.getsizeof(intA), sys.getsizeof(A)) # 运行输出:28 184
sys.getsizeof()的统计规则
这个函数的统计逻辑非常明确:仅计算传入对象自身直接占用的内存,不会递归统计容器内引用的其他对象的内存。不管是列表、字典、元组这类容器,调用getsizeof时都只会返回容器本身的内存开销,不会把容器里存的元素对象本身的大小算进去。
你测到单个int占28字节是64位CPython的正常表现:CPython的整数是封装后的结构体,除了存储实际数值,还要存引用计数、类型指针等元信息,远大于C语言原生int的内存占比。
184字节的列表内存构成
CPython的列表本质是存储对象指针的动态数组,自身内存分为两部分:
- 固定头部:64位Python3环境下占40字节,存储引用计数、类型指针、当前列表实际元素长度、元素存储区指针、已分配总容量这几个固定字段。
- 元素存储区:一块连续内存空间,这里不存实际的整数对象,只存指向整数对象的内存指针,64位系统下单条指针占8字节。
你看到的184字节计算逻辑非常清晰:184字节 = 40字节固定头部 + 18个指针位 * 8字节/个
之所以10个元素会分配18个指针位,是因为列表作为动态数组,扩容时会预分配冗余空间,避免每次新增元素都重新申请内存、拷贝全量数据,以此保证追加元素操作的均摊O(1)复杂度。你用列表推导生成10个元素的过程中,列表会自动扩容到可容纳18个指针的大小,这部分预分配的冗余空间也会被getsizeof统计到列表自身的占用里。
补充说明
你测试用到的0-9都属于CPython的小整数驻留对象:解释器启动时就会提前创建好-5到256区间内的所有整数,全局唯一复用,不会因为你把这些整数放进列表就额外生成新的整数对象。列表里存的只是指向这些预存整数的指针,整数本身的内存和列表自身的内存是完全独立的两块区域。
如果需要统计列表连带所有内部元素的总内存占用,需要自行递归遍历列表内的所有元素,累加每个对象的内存占用,同时做好循环引用的去重判断,getsizeof本身不提供递归统计能力。
内容的提问来源于stack exchange,提问作者ThreadBucks

