C-расширение в Python — проблема с утечкой памяти return Py_BuildValue()

У меня огромная проблема с утечкой памяти, связанная с C-расширением, которое я разрабатываю. В C у меня есть массив двойников с именем A и переменная типа int с именем AnotherIntVariable, которую я хочу передать Python. Что ж, в моем модуле расширения C я делаю следующее:

int i;  
PyObject *lst = PyList_New(len_A);  
PyObject *num;  
if(!lst)  
   return NULL;  
for(i=0;i<len_A;i++){  
   num=PyFloat_FromDouble(A[i]);  
   if(!num){  
      Py_DECREF(lst);  
      return NuLL;  
   }  
   PyList_SET_ITEM(lst,i,num);  
}  
free(A);  
return Py_BuildValue("Oi",lst,AnotherIntVariable)

Итак, в Python я получаю этот список и int следующим образом:

Pyt_A,Pyt_int=MyCModule.MyCFunction(...)

Где Pyt_A и Pyt_int — это список и целое число, которое я получаю из моего C-расширения «MyCModule» из функции «MyCFunction», которую я описал ранее.

Проблема в том, что в Python я использую этот массив Pyt_A (поэтому я использую Py_BuildValue вместо простого оператора return, чтобы сделать INCREF, чтобы на мгновение сохранить эту переменную из сборщика мусора), но затем мне нужно как-то разыменовать его, чтобы освободить выделенную память. Проблема в том, что я использую функцию MyCFunction несколько раз, и это приводит к утечке памяти, потому что я не знаю, как разыменовать массив, который я получаю в python, чтобы избавиться от него.

Я попытался просто вернуть массив, выполнив return lst в части кода C вместо Py_BuildValue("Oi",lst,AnotherIntVariable), но это приводит только к ошибке сегментации, когда я пытаюсь использовать его в python (вероятно, потому что сборщик мусора выполнил свою работу). .

... что мне здесь не хватает? Кто-нибудь может мне помочь?


person Néstor    schedule 01.04.2011    source источник
comment
не делает ли Pyt_A в python DECREF массив и удаляет его?   -  person highBandWidth    schedule 01.04.2011
comment
Я пробовал, но, похоже, это не работает. Я все еще получаю эту утечку памяти, о которой я говорю.   -  person Néstor    schedule 01.04.2011
comment
Кроме того, у вас есть возврат перед Py_BuildValue (Oi, lst, AnotherIntVariable), верно?   -  person highBandWidth    schedule 01.04.2011
comment
Да, я пропустил это в посте редактирование   -  person Néstor    schedule 01.04.2011
comment
что такое sys.getrefcount(Pyt_A) в python?   -  person highBandWidth    schedule 01.04.2011
comment
Значение sys.getrefcount(Pyt_A) равно 3.   -  person Néstor    schedule 01.04.2011


Ответы (2)


Если вы посмотрите документацию для Py_BuildValue (http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue) вы можете видеть, что в коде типа O указано, что счетчик ссылок переданного объекта увеличивается на единицу (Примечание: более ранний раздел на этой странице описывает код типа O для PyArg_ParseTuple, который не увеличивает счетчик ссылок, но здесь он также не актуален).

Итак, после вызова Py_BuildValue счетчик ссылок для вашего списка равен 2, но вы хотите, чтобы он был только 1.

Вместо того, чтобы возвращать результат Py_BuildValue напрямую, сохраните его в указатель PyObject, уменьшите счетчик ссылок lst, а затем верните результат.

В любом случае вам следует проверить результат вызова Py_BuildValue, так как вам также необходимо освободить num в случае, если Py_BuildValue не сработает (т.е. вернет NULL).

person ncoghlan    schedule 01.04.2011
comment
В документации теперь написано Счетчик ссылок на объект не увеличивается — так что же это? - person Eric; 26.03.2017
comment
Якорь целевой ссылки для Py_BuildValue изменился, поэтому ссылка вела вверх страницы, которая охватывает коды формата PyArg_ParseTuple. Я исправил ссылку и добавил примечание о потенциально запутанной записи. - person ncoghlan; 04.04.2017

Спасибо, что прояснил это, Игнасио, теперь это имеет такой смысл! Наконец, решение заключалось в том, чтобы вместо прямого возврата Py_BuildValue сделать:

free(A);  
PyObject *MyResult = Py_BuildValue("Oi",lst,AnotherIntVariable);  
Py_DECREF(lst);  
return MyResult

Оно работало завораживающе!

person Néstor    schedule 01.04.2011