I'm trying to track down a memory leak in a Python C extension, and am having trouble understanding why my code is giving the reference counts that it is.
Referring to the code example that follows, I'd like to understand the following,
Here's a short code example. (I'm using 64-bit Python 2.7 on Windows 10, using 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;
}
The above code generates a file that looks like the following:
1
1 2 1
118 119 118
119 120 119
120 121 120
1 2 1
0
-1825751608
I have annotated the end of each fprintf line in the code to show what number it displays.
You are printing the reference count of an object created by PyInt_FromLong(). You are surprised that it is 118 (or some other value), rather than 1.
This is because small numbers in Python can be "shared" meaning the runtime does not need to physically instantiate a new object every time you create a small number. This is for efficiency: most Python programs create a bunch of numbers like 0, 1, 2, 3 and so on, and sharing them reduces memory consumption and garbage collection time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With