【问题标题】:Understanding the memory allocation for a list了解列表的内存分配
【发布时间】:2015-04-16 11:46:36
【问题描述】:
>>> import sys
>>> sys.getsizeof([])
32
>>> sys.getsizeof([1])
36
>>> sys.getsizeof('')
25
>>> sys.getsizeof('a')
26
>>> sys.getsizeof('cam')
28

我对引用数组和紧凑数组有一个模糊的概念。

在 Python 中,列表是引用数组,因此它们使用更多内存来存储引用元素的内存位置。

我只能从上面的例子中推断出列表中的整数占用了额外的 4 个字节(32+4)。 字符串是字符数组。一个 unicode 字符应该占 2 位。

为什么一个空字符串占25个字节?

为什么一个空列表占用 32 个字节?

【问题讨论】:

  • 如果你会读C,你可能有兴趣看看List object source code
  • 顺便说一句,sys.getsizeof() 返回的那些数字是 bytes 而不是 bits。一个 Unicode 字符需要超过 2 位。 :)
  • 如果这是 Python 2,'a' 不是 Unicode 字符串。
  • 此外,Unicode 字符不应占用 2 个字节。有超过 100 万个 Unicode 字符,因此要将它们全部表示为单个代码单元,您的代码单元必须是 32 位。 (这是 UTF-32 表示。)当然你可以使用 UTF-16,但是在这种情况下,有些字符将是 2 个字节,有些是 4 个字节。或者你可以使用 UTF-8,或者任何其他编码,或者Python 3.3 使用的巧妙技巧,或者……

标签: python


【解决方案1】:

我只能从上面的例子中推断出列表中的整数占用了额外的 4 个字节(32+4)。

不,你想错了。

getsizeof 不是递归的。特别是,列表的大小只是列表“标题”的大小加上对其成员的引用数组。 (在通常的 CPython 实现中,这些引用是 PyObject * 指针)列表中有什么类型的对象没有区别,只是有多少。

(另外,请记住,列表通常在末尾有 slack。因此,一个包含 3 个元素的列表实际上可能有一个包含 4 个引用的数组,最后一个是空指针。)

同时,号码1 本身可能不会占用任何 存储空间。大多数 Python 实现都是小整数,所以 Python 中内置了一个 1 对象,无论您创建多少对数字 1 的引用,它们都只是对同一个对象的引用;您永远不会再创建 4 个字节。

为什么一个空字符串占25个字节?

一个字符串,类似于一个列表,一个字符串“头”,加上一个数组——只是一个字符数组,而不是对对象的引用。因为它们是不可变的,所以不需要松弛,这使得预测大小变得更容易。如果您的系统上的空字符串是 25 个字节,这意味着字符串头是 25 个字节,所以'abc' 将是 28 个字节,'abcde' 将是 30 个字节,依此类推。 (我在这里假设 Python 2.x 或 Python 3.3+;如果您使用的是 3.0-3.2,则每个字符实际上是 2 或 4 个字节。虽然 Python 3.3+ 中的字符串实际上有点复杂;请阅读如果你真的想知道来源。)

为什么一个空列表占用 32 个字节?

因为这就是列表标题的大小。


如果您想查看这些标头中的实际内容,您需要查看实现的源代码。假设您使用的是 CPython,您可以找到它here。 (这指向最新的主干版本,此时为 3.5alpha;您可以将 URL 中的 default 替换为 2.73.3 或您关心的任何版本。)

例如,列表在 C API 中属于 PyListObject 类型。您可以搜索源代码,也可以猜测listobject.h 可能是定义PyUnicodeObject 的文件。而there,您将看到定义类型的 C 结构。总结成员,有一个包含所有类型所需信息的通用标题(如引用计数)、一个指向实际数组的指针和一个分配的计数。

【讨论】:

  • 可能值得注意的是,作为Tichodroma points out,一个类可以将其__sizeof__定义为它想要的任何东西,我上面描述的实际上是__sizeof__的“默认”含义, 并且实际上只 保证 用于内置类型。
【解决方案2】:

Read the docs:

getsizeof() 调用对象的 __sizeof__ 方法并添加额外的垃圾收集器开销(如果对象由垃圾收集器管理)。

(重点是我的)

【讨论】:

  • 好点,它在__sizeof__ 中想要做什么完全取决于类型本身。在这种情况下,他碰巧问的是内置类型,它(至少在 3.x 中)保证了特定的含义。但总的来说,一切皆有可能。
【解决方案3】:

一个 unicode 字符应该占用 2 位。为什么是空字符串 占用 25 位?

因为 Python 字符串(字节或 unicode)是一个对象,而不是 unicode 字符。

>>> s = "a"
>>> type(s)
<type 'str'>
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> 

【讨论】:

    猜你喜欢
    • 2019-07-08
    • 2011-05-30
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 2018-03-22
    • 1970-01-01
    相关资源
    最近更新 更多