[絕對原創 轉載請注明出處]
Python源碼剖析
——PyListObject對象(3)
本文作者 : Robert Chen ([email protected] )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中維護的元素數量變化時,PyListObject中ob_size和allocated兩個變量的變化情況,從中窺見PyListObject對記憶體的使用和管理。
在PyListObject的輸出操作list_print中,我們添加了如下代碼,以觀察PyListObject對記憶體的管理:
printf("/nallocated=%d, ob_size=%d/n", op->allocated, op->ob_size);
觀察結果如圖9所示:
首先建立一個包含一個元素的list,這時ob_size和allocated都是1。這時list中擁有的所有記憶體空間都已被使用完,是以下次插入元素時就一定會調整list的記憶體空間了。
随後向list末尾追加元素2,可以看到,調整記憶體空間的動作發生了。allocated變成了5,而ob_size則變成了2,這裡明确地顯示出了PyListObject所采用的與C++中vector一樣的記憶體緩沖池政策。
繼續向list末尾追加元素3,4,5,當追加了元素5之後,list所擁有的記憶體空間又被使用完了,下一次再追加或插入元素時,記憶體空間調整的動作又會再一次發生。
如果這時在list中删除元素3,可以看到,ob_size發生了變化,而allocated則不發生變化,它始終如一地維護着目前list所擁有的全部記憶體數量。
接下來我們從圖10的結果中觀察一下PyListObject對象的建立和删除對于Python維護的PyListObject對象緩沖池的影響。
這次為了消除Python互動環境執行時對PyListObject對象緩沖池的影響,我們通過執行py腳本檔案來觀察,可以看到,當建立新的PyListObject對象時,如果緩沖池中有可用的PyListObject對象,則會使用緩沖池中的對象;而在銷毀一個PyListObject對象時,确實将這個對象放到了緩沖池中。