内存中列表的大小
我只是尝试了内存中python数据结构的大小。我写了以下代码片段:
import sys
lst1=[]
lst1.append(1)
lst2=[1]
print(sys.getsizeof(lst1), sys.getsizeof(lst2))
我在以下配置上测试了代码:
- Windows 7 64位,Python3.1:输出为:
52 40
所以lst1有52个字节,lst2有40个字节。 - 使用Python3.2的Ubuntu 11.4 32bit:输出为
48 32
- Ubuntu 11.4 32位Python2.7:
48 36
谁能向我解释为什么两个大小都不同,尽管它们都是包含1的列表?
在getsizeof函数的python文档中,我发现了以下内容:...adds an additional garbage collector
overhead if the object is managed by the garbage collector.
在我的小示例中可能是这种情况吗?
-
这是一个更完整的交互式会议,它将帮助我解释发生了什么(Windows XP 32位上的Python 2.6,但这并不重要):
>>> import sys >>> sys.getsizeof([]) 36 >>> sys.getsizeof([1]) 40 >>> lst = [] >>> lst.append(1) >>> sys.getsizeof(lst) 52 >>>
请注意,空列表要比其中的空列表小一些
[1]
。但是,添加元素后,它会变得更大。原因是
Objects/listobject.c
CPython源代码中的实现细节。空清单
[]
创建空列表时,不会为元素分配空间-在中可以看到PyList_New
。36字节是32位计算机上列表数据结构本身所需的空间量。列出一个元素
[1]
创建具有单个元素的列表时,除了列表数据结构本身所需的内存外,还为一个元素分配了空间。同样,可以在中找到PyList_New
。鉴于size
作为参数,它计算:nbytes = size * sizeof(PyObject *);
然后具有:
if (size <= 0) op->ob_item = NULL; else { op->ob_item = (PyObject **) PyMem_MALLOC(nbytes); if (op->ob_item == NULL) { Py_DECREF(op); return PyErr_NoMemory(); } memset(op->ob_item, 0, nbytes); } Py_SIZE(op) = size; op->allocated = size;
因此,我们看到有了
size = 1
,分配了一个指针的空间。4个字节(在我的32位框中)。追加到空列表
呼叫
append
空白清单时,会发生以下情况:PyList_Append
来电app1
app1
询问列表的大小(并得到0作为答案)app1
然后list_resize
以size+1
(在我们的例子中为1)进行呼叫list_resize
有一个有趣的分配策略,此注释从其来源进行了总结。
这里是:
/* This over-allocates proportional to the list size, making room * for additional growth. The over-allocation is mild, but is * enough to give linear-time amortized behavior over a long * sequence of appends() in the presence of a poorly-performing * system realloc(). * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... */ new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6); /* check for integer overflow */ if (new_allocated > PY_SIZE_MAX - newsize) { PyErr_NoMemory(); return -1; } else { new_allocated += newsize; }
让我们做一些数学
让我们看看如何达到在本文开头的会话中引用的数字。
因此,列表数据结构本身在32位上所需的大小为36个字节。对于单个元素,将为一个指针分配空间,因此这是4个额外的字节-总共40个字节。到目前为止还可以。
在
app1
空白列表中调用list_resize
时,会使用调用size=1
。根据的过度分配算法list_resize
,1之后的下一个最大可用大小为4,因此将分配4个指针的位置。4 * 4 = 16个字节,36 + 16 = 52。确实,一切都说得通:-)