内存中列表的大小

发布于 2021-01-29 15:08:29

我只是尝试了内存中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.在我的小示例中可能是这种情况吗?

关注者
0
被浏览
80
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    这是一个更完整的交互式会议,它将帮助我解释发生了什么(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.cCPython源代码中的实现细节。

    空清单

    []创建空列表时,不会为元素分配空间-在中可以看到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_resizesize+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。

    确实,一切都说得通:-)



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看