Python源码剖析[11] —— PyListObject对象(3)

来源:互联网 发布:明治奶粉淘宝 编辑:程序博客网 时间:2024/06/06 00:31

[绝对原创 转载请注明出处]

Python源码剖析

——PyListObject对象(3)

本文作者: Robert Chen (search.pythoner@gmail.com )

3.      PyListObject对象缓冲池

还记得吗,刚才我们按下了一个有趣的话题。没错,就是那个缓冲池,free_list。现在,是揭开它的神秘面纱的时候呢。我们想知道的问题是:free_list中所缓冲的PyListObject对象是从哪里获得的,是在何时创建的。答案就在一个PyListObject被销毁的过程中:

[listobject.c]

static void list_dealloc(PyListObject *op)

{

    int i;

    PyObject_GC_UnTrack(op);

    Py_TRASHCAN_SAFE_BEGIN(op)

    if (op->ob_item != NULL) {

        /* Do it backwards, for Christian Tismer.

           There's a simple test case where somehow this reduces

           thrashing when a *very* large list is created and

           immediately deleted. */

        i = op->ob_size;

        while (--i >= 0) {

            Py_XDECREF(op->ob_item[i]);

        }

        PyMem_FREE(op->ob_item);

    }

    if (num_free_lists < MAXFREELISTS && PyList_CheckExact(op))

        free_lists[num_free_lists++] = op;

    else

        op->ob_type->tp_free((PyObject *)op);

    Py_TRASHCAN_SAFE_END(op)

}

在销毁一个PyListObject的时候,当然要做的一件事是为list中的每一个元素改变其引用计数。然后,我们就来到了最有趣的部分。Python会检查我们开始提到的那个缓冲池,free_lists,查看其中缓存的PyListObject的数量是否已经满了,如果没有,就将该待删除的PyListObject放到缓冲池中,以备后用。现在一切真相大白了,那个在Python启动是空荡荡的缓冲池原来都是被本应该死去的PyListObject对象给填充了 ,在以后创建新的PyListObject的时候,Python会首先唤醒这些已经“死去”的PyListObject。感谢党,感谢政府,又给它们一个重新做“人”的机会:)但是,需要指出,这里缓冲的仅仅是PyListObject对象,而没有这个对象曾经拥有的PyObject*列表,因为这些PyObject指针的引用计数已经减少了,这些指针所指的对象都要各奔前程,或生存,或毁灭,不再被PyListObject所给与的那个引用计数所束缚。PyListObject如果继续维护一个指向这些对象的指针的列表,就可能产生悬空指针的问题。所以,PyObject*列表所占用的空间必须归还给系统。

看一下我们刚刚创建的PyListObject的最后归宿:

在下一次创建PyListObject时,这个PyListObject将重新被唤醒,重新分配PyObject*列表占用的内存,重新拥抱新的对象。放眼四周,曾经所拥有过的那些对象,有的已经容颜苍老,有的已经烟消云散,是否有一种“无私人非事事休,欲语泪先流”的感慨呢?:)

4.      Hack PyListObject

首先我们来观察在PyListObject中维护的元素数量变化时,PyListObjectob_sizeallocated两个变量的变化情况,从中窥见PyListObject对内存的使用和管理。

PyListObject的输出操作list_print中,我们添加了如下代码,以观察PyListObject对内存的管理:

printf("/nallocated=%d, ob_size=%d/n", op->allocated, op->ob_size);

观察结果如图9所示:

    首先创建一个包含一个元素的list,这时ob_sizeallocated都是1。这时list中拥有的所有内存空间都已被使用完,所以下次插入元素时就一定会调整list的内存空间了。

随后向list末尾追加元素2,可以看到,调整内存空间的动作发生了。allocated变成了5,而ob_size则变成了2,这里明确地显示出了PyListObject所采用的与C++vector一样的内存缓冲池策略。

继续向list末尾追加元素345,当追加了元素5之后,list所拥有的内存空间又被使用完了,下一次再追加或插入元素时,内存空间调整的动作又会再一次发生。

如果这时在list中删除元素3,可以看到,ob_size发生了变化,而allocated则不发生变化,它始终如一地维护着当前list所拥有的全部内存数量。

接下来我们从图10的结果中观察一下PyListObject对象的创建和删除对于Python维护的PyListObject对象缓冲池的影响。

次为了消除Python交互环境执行时对PyListObject对象缓冲池的影响,我们通过执行py脚本文件来观察,可以看到,当创建新的PyListObject对象时,如果缓冲池中有可用的PyListObject对象,则会使用缓冲池中的对象;而在销毁一个PyListObject对象时,确实将这个对象放到了缓冲池中。

原创粉丝点击