整数类型比较简单,定义如下(Python2.x中,在Python3.x中int与long合并)
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
整数在程序执行过程中创建销毁特别频繁,Python使用整数对象池(几乎所有对象都是)来避免内存的频繁申请释放。
分为小整数对象池、通用整数对象池。
1、整数对象池基础结构
#define BLOCK_SIZE 1000 // 内存块的大小
#define BHEAD_SIZE 8 // 容纳64位指针
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject)) // 容纳整数对象的个数
typedef struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
}PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
如图所示,objects实质为N个整数对象的数组。
block_list通过单向列表维护整个对象池,free_list通过单向列表维护所有未使用内存。
2、通用整数对象池
创建通用整数对象池时,会使得objects中每个整数对象的ob_type指向前一个整数对象,从而形成单向列表。
在freelist指向的地方创建一个整数对象,然后free_list指向下一个空闲空间。当一个整数对象的引用计数为0时,该对象的ob_type指向freelist所指空间,然后freelist指向该处,从而模拟出该对象已被删除。
static void int_dealloc(PyIntObject *v)
{
if (PyInt_CheckExact(v)) {
v->ob_type = (struct _typeobject *)free_list;
free_list = v;
}
else
v->ob_type->tp_free((PyObject *)v);
}
多次创建删除后,对象的ob_types是跨PyIntBlock的,从而freelist也可在多个PyIntBlock之间来回跳动,不会出现内存泄漏等情况。
3、小整数对象池小整数对象池保存了[-5, 100)之间的整数对象,在python开始运行时初始化,指针按照顺序存放于small_ints[]中。
#define NSMALLPOSINTS 100
#define NSMALLNEGINTS 5
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
整体结构如下图所示
-----------------------------------------------------------------------------------------------------------------
运行时,先查看是否在小对象池中,有则直接使用;无则在通用对象池中新建。
小对象池中整数对象是唯一的,即 a=5 与 b=5 指向同一对象。
大对象池中整数对象不一定,a=1234 与 b=1234不一定指向同一对象,与创建方式有关。