2016-11-19 51 views
2

我試圖追查一個Python C擴展中的內存泄漏,並且無法理解爲什麼我的代碼給出了它的引用計數。Python引用計數:爲什麼不是Py_REFCNT(o)= 1?

參照下面的代碼例如,我想了解以下,

  1. 爲什麼在118在塊2開始的引用計數,而不是1? (注意方塊2對方塊1使用不同的變量。)
  2. 爲什麼方塊4中的參考計數從120開始而不是1? (請注意,塊4使用與塊1相同的變量名稱。)
  3. 鑑於塊4中的引用計數不是從1開始的,爲什麼它在塊5的開始時是1?
  4. 最後打印的號碼似乎是內存地址。爲什麼不打印0的引用計數?

下面是一個簡短的代碼示例。 (我使用的64位的Python 2.7在Windows 10,使用VS2012)

// test.c : Test python reference counting 
// 

#include <stdlib.h> 
#include <Python.h> 

int main() 
{ 
    PyObject *PythonFunctionInput; 
    FILE *fid; 

    PyObject *tempPyObj; 
    PyObject *tempPyObjInt; 
    const int x = 1; // This is to mimic data in my actual application 
    const int y = 1; 
    const int z = 1; 


    Py_InitializeEx(0); 
    PythonFunctionInput = PyDict_New(); 

    fid = fopen("TestOutputFile.txt", "wt"); 

    fprintf(fid,"%d\n",(int)Py_REFCNT(PythonFunctionInput)); // 1 

    // Block 1 
    tempPyObj = PyString_FromString("name1"); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 1 
    PyDict_SetItemString(PythonFunctionInput,"fieldname1",tempPyObj); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 2 
    Py_DECREF(tempPyObj); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(tempPyObj)); // 1 

    // Block 2 
    tempPyObjInt = PyInt_FromLong((long)x); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObjInt)); // 118 why? 
    PyDict_SetItemString(PythonFunctionInput,"fieldname2",tempPyObjInt); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObjInt)); // 119 
    Py_DECREF(tempPyObjInt); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(tempPyObjInt)); //118 

    // Block 3 
    tempPyObjInt = PyInt_FromLong((long)y); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObjInt)); // 119 
    PyDict_SetItemString(PythonFunctionInput,"fieldname3",tempPyObjInt); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObjInt)); // 120 
    Py_DECREF(tempPyObjInt); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(tempPyObjInt)); // 119 

    // Block 4 
    tempPyObj = PyInt_FromLong((long)z); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 120 why? 
    PyDict_SetItemString(PythonFunctionInput,"fieldname4",tempPyObj); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 121 
    Py_DECREF(tempPyObj); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(tempPyObj)); // 120 

    // Block 5 
    tempPyObj = PyString_FromString("name5"); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 1 why? 
    PyDict_SetItemString(PythonFunctionInput,"fieldname5",tempPyObj); 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // 2 
    Py_DECREF(tempPyObj); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(tempPyObj)); // 1 

    // Block 6 
    Py_DECREF(PythonFunctionInput); 
    fprintf(fid,"%d\n",(int)Py_REFCNT(PythonFunctionInput)); // 0 
    fprintf(fid,"%d ",(int)Py_REFCNT(tempPyObj)); // why not 0 ? 

    Py_Finalize(); 

    fclose(fid); 

    return 0; 
} 

上面的代碼生成看起來如下文件:

1 
1 2 1 
118 119 118 
119 120 119 
120 121 120 
1 2 1 
0 
-1825751608 

我已註釋的端代碼中的每個fprintf行顯示它顯示的數字。

+1

您的最後兩張照片是已經處理的對象的重新計數。沒有這樣的訪問是有效的;沒有意義背後的事實,一個返回零,一個沒有。我相信甚至有可能通過嘗試訪問不再存在的對象來使程序崩潰。 – jasonharper

回答

2

您正在打印由PyInt_FromLong()創建的對象的引用計數。您驚訝於它是118(或其他值),而不是1.

這是因爲Python中的小數字可以「共享」,這意味着運行時不需要每次創建時都物理實例化一個新對象少數。這是爲了提高效率:大多數Python程序都創建了一堆數字,如0,1,2,3等,並共享它們減少了內存消耗和垃圾收集時間。

+1

太好了,這給了'PyInt_FromLong'的文檔註釋提前一些上下文來實例化從-5到256的數字,我顯然沒有理解後果。 –

相關問題