Python引用计数:为什么Py_REFCNT(o)= 1?

时间:2016-11-19 04:54:12

标签: python python-2.7

我正在尝试追踪Python C扩展中的内存泄漏,并且无法理解为什么我的代码会提供引用计数。

参考下面的代码示例,我想了解以下内容,

  1. 为什么Block 2中的引用计数从118开始而不是1? (请注意,块2使用与块1不同的变量。)
  2. 为什么Block 4中的引用计数从120开始而不是1? (请注意,块4使用与块1相同的变量名称。)
  3. 鉴于第4组中的引用计数未从1开始,为什么在第5块开始时它现在为1?
  4. 打印的最后一个数字似乎是一个内存地址。为什么不打印引用计数为0?
  5. 这是一个简短的代码示例。 (我在Windows 10上使用64位Python 2.7,使用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 个答案:

答案 0 :(得分:2)

您正在打印由PyInt_FromLong()创建的对象的引用计数。你很惊讶它是118(或其他一些值),而不是1。

这是因为Python中的小数字可以“共享”,这意味着每次创建一个小数字时,运行时不需要物理实例化新对象。这是为了提高效率:大多数Python程序创建了一堆数字,如0,1,2,3等等,共享它们可以减少内存消耗和垃圾收集时间。