为啥相同对象的内存空间分配不同?

Posted

技术标签:

【中文标题】为啥相同对象的内存空间分配不同?【英文标题】:Why memory space allocation is different for the same objects?为什么相同对象的内存空间分配不同? 【发布时间】:2021-05-16 14:19:59 【问题描述】:

我正在试验 Python 如何分配内存,所以发现了同样的问题,例如 Size of list in memory 和 Eli 以更好的方式进行了描述。他的回答让我产生了新的疑问,我检查了1 + [] and [1] 的大小,但正如您在代码 sn-p 中看到的那样,它是不同的。如果我没记错内存空间分配应该是一样的。但事实并非如此。任何人都可以帮助我理解吗?

>>> import sys
>>> sys.getsizeof(1)
    28
>>> sys.getsizeof([])
    64
>>> 28 + 64
    92
>>> sys.getsizeof([1])
    72

【问题讨论】:

我真的不明白你不明白什么。你在哪里比较相同对象的大小......但也许你缺少的是sys.getsizeof 只给你对象本身的大小,而不是任何其他可能被引用的对象论点。所以,[1] 的大小只是列表对象,它在后台包含一个 PyObject 指针数组。 这是因为列表包含对其他对象的引用。引用的sizeof 是 72-64=8。所以你可以把很多大元素放到你的列表中,但是列表的大小仍然是 64+(8*len) (我在这里跳过了关于列表调整大小分配的步骤,所以这只是 mostly 准确)。每个对象依次将占用所需的空间(在您的情况下为 28 个) 添加另一个怪癖,而 1 是 28 字节大,它是 CPython 中的 cached integer。只有引用实际上需要另一个分配。不过,该引用有 8 个字节大,array 存储可能会更有效。 @YannVernier 缓存与它无关(除非您指的是可从列表引用访问的总内存)。列表的元素总是对其他对象的引用。 它仅与列出的数字间接相关,但与考虑使用多少内存以及发生哪些动态分配有关。缓存的整数是预先分配的,小整数保持 28 字节(在那个 64 位系统上),长整数可以更大。由于询问者希望整数驻留在列表中,因此知道它实际存储在哪里可能是相关的。旁注:虽然列表只保存引用,但数组确实保存值。这带来了另一个成本,因为提取它们必须转换为 Python 对象。 【参考方案1】:

列表运行所需的最少信息是什么?

    某种***列表对象,包含对类信息(方法、类型信息等)的引用,以及列表自身的实例数据 存储在列表中的实际对象

...这会让你得到你期望的大小。但够了吗?

一个固定大小的列表对象只能跟踪固定数量的列表条目:传统上只有一个(头)或两个(头和尾)。 向列表中添加更多的条目并不会改变列表对象本身的大小,因此必须有一些额外的信息:列表元素之间的关系。

可以将这些信息存储在每个 Object 中(这称为侵入式列表),但限制非常严格:每个 Object 一次只能存储在一个列表中。

由于 Python 列表显然不是那样的行为,我们知道这个额外的信息并不已经在列表元素中,而且它不能在列表对象中,所以它必须存储在其他地方。这会增加列表的总大小。

注意。我故意使这个论点相当抽象。您可以通过几种不同的方式实现 list,但它们都不能避免为元素关系提供一些额外的存储空间,即使表示不同。

【讨论】:

这个答案的大部分描述了一个链表,而不是一个 Python 列表(它通常实现为一个动态大小的参考数组)。 在这种情况下,额外的分配是参考数组本身以及其中的任何松弛分配。关键是,无论实现细节如何,都需要存储比 OP 预期更多的信息。这就是为什么我非常小心地准确地说出来的原因。也许我应该把最后一段移到顶部?

以上是关于为啥相同对象的内存空间分配不同?的主要内容,如果未能解决你的问题,请参考以下文章

C++面向对象编程:对象的内存分配与静态成员

java-实例变量和类变量的区别

C++ new的时候,为啥会存在内存分配会失败的情况?啥导致的呢?

析构函数为啥能释放对象内存?

java中的内存分配

PHP对象在内存堆栈中的分配