31

在 64 位系统上,Python 中的整数占用 24 个字节。这是 64 位整数在例如 C 中所需内存的 3 倍。现在,我知道这是因为 Python 整数是对象。但是额外的内存有什么用呢?我有我的猜测,但很高兴知道。

4

2 回答 2

39

请记住,Pythonint类型没有像 C 那样的有限范围int;唯一的限制是可用内存。

内存用于存储值、整数存储的当前大小(存储大小是可变的以支持任意大小)和标准 Python 对象簿记(对相关对象的引用和引用计数)。

您可以查找longintrepr.h源代码(Python 3int类型传统上称为longPython 2 中的类型);它有效地使用了PyVarObjectC 类型来跟踪整数大小:

struct _longobject {
        PyObject_VAR_HEAD
        digit ob_digit[1];
};

ob_digit数组存储 15 位或 30 位宽的“数字”(取决于您的平台);所以在我的 64 位 OS X 系统上,一个不超过 (2 ^ 30) - 1 的整数使用 1 个“数字”:

>>> sys.getsizeof((1 << 30) - 1)
28

但如果您在数字中使用 2 个 30 位数字,则需要额外的 4 个字节,等等:

>>> sys.getsizeof(1 << 30)
32
>>> sys.getsizeof(1 << 60)
36
>>> sys.getsizeof(1 << 90)
40

然后基数 24 字节是PyObject_VAR_HEAD结构,保存对象大小、引用计数和类型指针(在我的 64 位 OS X 平台上每个 8 字节/64 位)。

在 Python 2 上,整数 <=sys.maxint但 >=-sys.maxint - 1使用更简单的结构存储,仅存储单个值:

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

因为这使用PyObject而不是结构PyVarObject中没有ob_size字段,并且内存大小仅限于 24 字节;8 表示long值,8 表示引用计数,8 表示类型对象指针。

于 2014-04-11T15:23:51.563 回答
4

从 longintrepr.h 中,我们看到一个 Python 'int' 对象是用这个 C 结构定义的:

struct _longobject {
        PyObject_VAR_HEAD
        digit ob_digit[1];
};

数字是一个 32 位无符号值。大部分空间由可变大小的对象标头占用。从object.h中,我们可以找到它的定义:

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

我们可以看到我们正在使用 Py_ssize_t,假设 64 位系统为 64 位,将“数字”的计数存储在值中。这可能是浪费。我们还可以看到,一般的对象头有一个 64 位的引用计数,以及一个指向对象类型的指针,这也将是一个 64 位的存储空间。引用计数对于 Python 知道何时释放对象是必要的,指向对象类型的指针对于知道我们有一个 int 而不是,比如说,一个字符串是必要的,因为 C 结构无法测试来自任意指针的对象。

_PyObject_HEAD_EXTRA 在大多数 python 构建中被定义为空,但如果构建启用该选项,则可用于在堆上存储所有 Python 对象的链接列表,每个使用另外两个 64 位指针。

于 2014-04-11T16:11:56.810 回答