Почему у моего расширения Python C утечка памяти?

Функция ниже принимает дескриптор файла 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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
0
4 737
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

PyDict_New() возвращает новую ссылку, проверьте документы на наличие PyDict. Поэтому, если вы увеличите счетчик ссылок сразу после его создания, у вас будет две ссылки на него. Один передается вызывающей стороне, когда вы возвращаете его в качестве значения результата, но другой никогда не уходит.

Вам также не нужно увеличивать pyTimeList. Это твое, когда ты его создаешь. Однако вам нужно уменьшить его, но вы уменьшаете его только один раз, поэтому он также просочился.

Вам также не нужно вызывать Py_INCREF на pyDB. Это заимствованная ссылка, и она не исчезнет, ​​пока ваша функция не вернется, потому что на нее по-прежнему ссылаются в нижнем фрейме стека.

Только если вы хотите сохранить ссылку где-то в другой структуре, вам нужно увеличить счетчик ссылок.

Ср. Документы API

Торстен, спасибо, я только что выучил больше из ваших 4 абзацев, а потом все утро смотрел на документы. Я проверю все мои заимствованные и верну новые ссылки.

Mark 08.12.2008 23:40

ОТ: Использование последовательных вызовов 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.

Другие вопросы по теме