Python源码剖析1-整数对象PyIntObject

1、PyIntObject 对象

[intobject.h]
typedef struct {
	PyObject_HEAD
    long ob_ival;
} PyIntObject

PyIntObject是一个不可变(immutable)对象。Python内部也大量的使用整数对象,我们在自己的代码中也会有大量的创建销毁整型对象的操作,因此单独的维护整形对象并对其申请内存和释放内存是不现实的。Python给出的解决方案是将整形对象通过一定的结构连接在一起的整数对象系统:整数对象池,一个整形对象的缓冲池机制。

对象创建:

  • PyInt_FromString
  • PyInt_FromLong
  • PyInt_FromUnicode

其中,后两种方法实际上是先转换成浮点数,然后再调用PyInt_FromFloat,这实际上是 Adaptor Pattern 的思想:对核心函数进行接口转换。

2、小整数对象

小整数会频繁的使用。在 Python 中,所有的对象都是存活在系统堆上。这样的操作不仅大大降低了运行效率,而且会在系统堆上造成内存碎片。

所以解决方法就是对小整数使用对象池技术,正是因为使用缓冲池,PyIntObject才是不可变对象。对象池中的每一个对象可以被安全的共享。那么,多小才算小整数?默认的范围是-5到256,这个值不可以动态修改,要想修改只能修改源代码然后重新编译。

3、大整数对象

对于大整数对象,是一次申请一块内存,这块内存用PyIntBlock结构体管理,该结构体中中有一个PyIntObject数组(会链表形式维护)来供大整数对象使用,还有一个用于形成链表的指向下一个block的指针。如果这一整块内存都祸祸光了(默认一个block可以存放82个int对象),就再申请一个PyIntBlock,然后用一个单向链表维护所有的PyIntBlock,这个链表就是大整数对象缓冲池两个重要变量其中之一:block_list指针。

这个 block 链表维护的是一整块block,是block级别的,我要使用的是PyIntObject,每次使用的话总不能进到block去遍历数组去找到一个还没使用的PyIntObject吧,所以下一个大整数缓冲池至关重要的变量就是free_list指针,这个指向一个链表,链表中的所有元素是PyIntObject。

4、引用计数:ob_refcnt

Python通过管理对象的引用计数来决定对象在内存中的存在与否,Python一切皆对象,所有对象都有一个 ob_refcnt 变量。这个变量维护这对象的引用计数,也决定这对象的存在与消亡。

对于某一对象A,当有一个新的PyObject * 引用该对象时,A的引用计数应该增加,而当PyObject * 被删除时, 引用计数应该减少。计数为0时,A 可以从堆上被删除,释放内存。

5、运行时整数对象及其类型之间的关系

对于int(10) 的 ob_refcnt 来说可以理解为多个ref 引用了这个对象,ob_type 是指向其类型对象的指针,ob_ival 是具体数值。

int(10) 是PyIntObject 的实例对象,比PyObject 多一个ob_ival 成员,PyInt_Type、PyBaseObject_Type、PyType_Type 都是PyTypeObject 的实例对象。PyInt_Type 的 tp_base 指向其基类对象 PyBaseObject_Type,而他们的 ob_type 都指向 PyType_Type。

6、不同PyintBlock中空闲内存的互连:tp_dealloc

Python中不同对象在销毁时会进行不同的动作,销毁动作在与对象对应的类对象中被定义,这个关键操作就是类型对象中的tp_dealloc。

static void int_dealloc(PyIntObject *v)
(
    if (PyInt_CheckExact(v))  {
        v->ob_type = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        v->obtype->tp_free((Pyobject *)v);

tp_dealloc的作用:

由block_list维护的PyIntBlock链表中的内存实际是所有的大整数对象共同分享的。当一个PyIntObject对象被销毁时,它所占的内存并不会被释放,归还给系统,Python会继续保留着。将来提供给别的PyIntObject使用,所以Python应该将其链入了free_list所维护的自由内存链表。

以下是创建可删除PyIntObject对象2、3、4的过程中,内存中对象PyIntObject以及free_list指针的变化:

不同PyIntBlock对象中空闲内存的互连是在int_dealloc被调用时实现的。

 注意:在int_dealloc中,永远不会向系统堆交还任何内存,一旦系统堆中的某块内存被python申请用于整数对象,那么这块内存在Python进程结束之前,将永远不会得到释放。

由于内存共享,Python用于实现该对象池的内存与历史上创建的整数对象的个数无关,而仅仅与同一时刻共存的整数对象个数的最大值有关

————————————————

参考:

  • Python源码剖析(陈孺)

猜你喜欢

转载自blog.csdn.net/qq_19446965/article/details/128168253