Примитивный модуль на C для Python течёт по памяти?

Доброго времени суток! Написал вчера кое-какую утилиту, используя модуль PyJACK, и внезапно обнаружил странную утечку памяти, ну не заметить её сложно, потому что в лупе через полминуты вся память вместе со свопом — ёк. Проблема в том, что Python list, который возвращает модуль на сях остаётся в памяти, даже если какой-нибудь del вызвать. Прошу пояснить, это я чего-то не знаю/не понимаю, или это багофича, и как вообще с этим быть? Python 2.7, xubuntu 12.04.



Итак, вот минимальный пример проблемы.



cmodule.c

#include <Python.h><br><br>
static PyObject *<br>
test(PyObject *self, PyObject *args)<br>
{<br>
    static PyObject *list;<br>
    list = PyList_New(0);<br>
    PyList_Append(list, Py_BuildValue("s", "nya"));<br>
    return list;<br>
}<br><br>
static PyMethodDef<br>
ModuleMethods[] = {<br>
    {"test", test, METH_VARARGS, "Test for memoty leak."},<br>
    {NULL, NULL, 0, NULL}<br>
};<br><br>
PyMODINIT_FUNC<br>
initcmodule(void)<br>
{<br>
    (void) Py_InitModule("cmodule", ModuleMethods);<br>
}<br>




setup.py

from setuptools import setup, Extension<br>
setup(ext_modules=[Extension('cmodule', ['src/cmodule.c'], include_dirs=['src'])])<br>




И собственно запускают это:

memleak_test.py

import cmodule<br>
while True: cmodule.test()<br>


И смотрю как память течёт водопадом.



Как мне видится — это GC Python-а почему-то недоедает, или мой код так написан, что не попадает в его график работы?
  • Вопрос задан
  • 4468 просмотров
Решения вопроса 1
@MikhailEdoshin
Со списком все в порядке, проблема во взаимодействии функций. Py_BuildValue возвращает new reference, то есть объект со счетчиком ссылок, установленным в единицу, которым теперь владеет ваш код. Функция же PyList_Append не забирает у вас этот объект, а тоже увеличивает счетчик ссылок еще на единицу. Теперь у полученной строки будет два владельца — список и ваш код. Когда вы удалите список, он честно отнимет свою единичку от счетчика ссылок, но ваша-то там останется, поэтому Питон будет думать, что кто-то где-то эту строку еще использует.

Вам нужно или явно уменьшить счетчик:

PyObject *list, *item; /* static, право, ни к чему */
list = PyList_New(0);
item = Py_BuildValue("s"; "test");
PyList_Append(list, item);
Py_DECREF(item);
return list;

или же использовать функцию, которая не увеличивает счетчик (steals reference):

PyObject *list;
list = PyList_New(1); /* оставляем место для элемента */
PyList_SET_ITEM(list, Py_BuildValue("s"; "test"));
return list;

(В данном случае я использовал макрос PyList_SET_ITEM, который как раз подходит для заполнения новых списков.)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы