Функция ниже принимает дескриптор файла Python, считывает упакованные двоичные данные из файла, создает словарь Python и возвращает его. Если я буду зацикливать его бесконечно, он будет постоянно потреблять оперативную память. Что не так с моим RefCounting?
static PyObject* __binParse_getDBHeader(PyObject *self, PyObject *args){
PyObject *o; //generic object
PyObject* pyDB = NULL; //this has to be a py file object
if (!PyArg_ParseTuple(args, "O", &pyDB)){
return NULL;
} else {
Py_INCREF(pyDB);
if (!PyFile_Check(pyDB)){
Py_DECREF(pyDB);
PyErr_SetString(PyExc_IOError, "argument 1 must be open file handle");
return NULL;
}
}
FILE *fhDB = PyFile_AsFile(pyDB);
long offset = 0;
DB_HEADER *pdbHeader = malloc(sizeof(DB_HEADER));
fseek(fhDB,offset,SEEK_SET); //at the beginning
fread(pdbHeader, 1, sizeof(DB_HEADER), fhDB );
if (ferror(fhDB)){
fclose(fhDB);
Py_DECREF(pyDB);
PyErr_SetString(PyExc_IOError, "failed reading database header");
return NULL;
}
Py_DECREF(pyDB);
PyObject *pyDBHeader = PyDict_New();
Py_INCREF(pyDBHeader);
o=PyInt_FromLong(pdbHeader->version_number);
PyDict_SetItemString(pyDBHeader, "version", o);
Py_DECREF(o);
PyObject *pyTimeList = PyList_New(0);
Py_INCREF(pyTimeList);
int i;
for (i=0; i<NUM_DRAWERS; i++){
//epochs
o=PyInt_FromLong(pdbHeader->last_good_test[i]);
PyList_Append(pyTimeList, o);
Py_DECREF(o);
}
PyDict_SetItemString(pyDBHeader, "lastTest", pyTimeList);
Py_DECREF(pyTimeList);
o=PyInt_FromLong(pdbHeader->temp);
PyDict_SetItemString(pyDBHeader, "temp", o);
Py_DECREF(o);
free(pdbHeader);
return (pyDBHeader);
}
Спасибо, что посмотрели,
ЛарсенMTL






PyDict_New() возвращает новую ссылку, проверьте документы на наличие PyDict. Поэтому, если вы увеличите счетчик ссылок сразу после его создания, у вас будет две ссылки на него. Один передается вызывающей стороне, когда вы возвращаете его в качестве значения результата, но другой никогда не уходит.
Вам также не нужно увеличивать pyTimeList. Это твое, когда ты его создаешь. Однако вам нужно уменьшить его, но вы уменьшаете его только один раз, поэтому он также просочился.
Вам также не нужно вызывать Py_INCREF на pyDB. Это заимствованная ссылка, и она не исчезнет, пока ваша функция не вернется, потому что на нее по-прежнему ссылаются в нижнем фрейме стека.
Только если вы хотите сохранить ссылку где-то в другой структуре, вам нужно увеличить счетчик ссылок.
Ср. Документы API
ОТ: Использование последовательных вызовов PyList_Append - проблема производительности. Поскольку вы заранее знаете, сколько результатов вы получите, вы можете использовать:
PyObject *pyTimeList = PyList_New(NUM_DRAWERS);
int i;
for (i=0; i<NUM_DRAWERS; i++){
o = PyInt_FromLong(pdbHeader->last_good_test[i]);
PyList_SET_ITEM(pyTimeList, i, o);
}
Обратите внимание, что вы не можете уменьшить счетчик ссылок o после вызова PyList_SET_ITEM, потому что он «крадет» ссылку. Проверьте документы.
Я не знаю о Python-C. Однако мой опыт работы с подсчетом ссылок COM говорит о том, что вновь созданный объект с подсчетом ссылок имеет счетчик ссылок 1. Итак, ваш Py_INCREF (pyDB) после PyArg_ParseTuple (args, «O», & pyDB) и PyObject * pyDBHeader = PyDict_New (); виноваты. Их количество ссылок уже равно 2.
Торстен, спасибо, я только что выучил больше из ваших 4 абзацев, а потом все утро смотрел на документы. Я проверю все мои заимствованные и верну новые ссылки.