首页 > 解决方案 > 列表python的内存大小

问题描述

我正在用列表做一些实验,然后我想到了自引用列表。我搜索了 SO 并得到了一些关于它的基本问题的回答。但是当我试图获取不同长度的自引用列表的内存大小时,我发现了一个有趣的模式

重现代码:

import sys

memory_size = {}

for length in range(50):
    lst = []
    for length_loop in range(length):
        lst.append(lst)
    memory_size[length] = sys.getsizeof(lst)

memory_size 的值:

{0: 64, 1: 96, 2: 96, 3: 96, 4: 96, 5: 128, 6: 128, 7: 128, 8: 128, 9: 192, 10: 192, 11: 192, 12: 192, 13: 192, 14: 192, 15: 192, 16: 192, 17: 264, 18: 264, 19: 264, 20: 264, 21: 264, 22: 264, 23: 264, 24: 264, 25: 264, 26: 344, 27: 344, 28: 344, 29: 344, 30: 344, 31: 344, 32: 344, 33: 344, 34: 344, 35: 344, 36: 432, 37: 432, 38: 432, 39: 432, 40: 432, 41: 432, 42: 432, 43: 432, 44: 432, 45: 432, 46: 432, 47: 528, 48: 528, 49: 528}

在绘制上述数据点时

在此处输入图像描述

Python 3.7.3 (default, Mar 27 2019, 16:54:48)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

为什么自引用列表的内存大小在一定长度范围内保持不变,并在一定长度后增加?

而且内存大小的增加也是不同的

标签: pythonpython-3.xlistpython-internals

解决方案


如果列表是通过 append 增长的,则它们始终会显示此模式。

要理解的关键点,sys.getsizeof 不考虑列表中引用的对象,只考虑列表对象本身的大小。现在,Pythonlist对象在底层实现为数组列表,因此基本上有一个 PyObject 标头(例如,16 字节开销或类似的),然后是一个原始的 PyObject 指针数组(这就是为什么它们可以是异构的,并引用自己)。

这个底层数组是overallocated的,并且它被重新调整大小以保证摊销的恒定时间.append操作。换句话说,Pythonlist对象已经摊销了 constant-time .append,因此执行类似for x in range(N): my_list.append(0)线性时间操作的操作,因为底层缓冲区不会在每次迭代时重新分配。

看,您会看到与任何对象相同的模式,例如None

In [24]: import sys
    ...:
    ...: memory_size = {}
    ...:
    ...: for length in range(50):
    ...:     lst = []
    ...:     for length_loop in range(length):
    ...:         lst.append(None)
    ...:     memory_size[length] = sys.getsizeof(lst)
    ...:

In [25]: memory_size
Out[25]:
{0: 72,
 1: 104,
 2: 104,
 3: 104,
 4: 104,
 5: 136,
 6: 136,
 7: 136,
 8: 136,
 9: 200,
 10: 200,
 11: 200,
 12: 200,
 13: 200,
 14: 200,
 15: 200,
 16: 200,
 17: 272,
 18: 272,
 19: 272,
 20: 272,
 21: 272,
 22: 272,
 23: 272,
 24: 272,
 25: 272,
 26: 352,
 27: 352,
 28: 352,
 29: 352,
 30: 352,
 31: 352,
 32: 352,
 33: 352,
 34: 352,
 35: 352,
 36: 440,
 37: 440,
 38: 440,
 39: 440,
 40: 440,
 41: 440,
 42: 440,
 43: 440,
 44: 440,
 45: 440,
 46: 440,
 47: 536,
 48: 536,
 49: 536}

只是为了说服你,这里是你的自我参考列表:

In [26]: import sys
    ...:
    ...: memory_size = {}
    ...:
    ...: for length in range(50):
    ...:     lst = []
    ...:     for length_loop in range(length):
    ...:         lst.append(lst)
    ...:     memory_size[length] = sys.getsizeof(lst)
    ...:

In [27]: memory_size
Out[27]:
{0: 72,
 1: 104,
 2: 104,
 3: 104,
 4: 104,
 5: 136,
 6: 136,
 7: 136,
 8: 136,
 9: 200,
 10: 200,
 11: 200,
 12: 200,
 13: 200,
 14: 200,
 15: 200,
 16: 200,
 17: 272,
 18: 272,
 19: 272,
 20: 272,
 21: 272,
 22: 272,
 23: 272,
 24: 272,
 25: 272,
 26: 352,
 27: 352,
 28: 352,
 29: 352,
 30: 352,
 31: 352,
 32: 352,
 33: 352,
 34: 352,
 35: 352,
 36: 440,
 37: 440,
 38: 440,
 39: 440,
 40: 440,
 41: 440,
 42: 440,
 43: 440,
 44: 440,
 45: 440,
 46: 440,
 47: 536,
 48: 536,
 49: 536}

各个大小的差异归结为 Python 版本和系统架构(例如,在 32 位系统上,指针是 4 个字节而不是 8 个字节,并且不同的 Python 版本可以自由更改实现细节,例如空列表)。请注意,如果我使用另一个环境,以上是在 Python 3.7 上运行的:

(base) juanarrivillaga@173-11-109-137-SFBA ~ % python -c "import sys; print(f'{sys.version}\nEmpty List Size: {sys.getsizeof([])}')"
3.8.1 (default, Jan  8 2020, 16:15:59)
[Clang 4.0.1 (tags/RELEASE_401/final)]
Empty List Size: 56

推荐阅读