【问题标题】:Python C Wrapper Memory LeakPython C 包装器内存泄漏
【发布时间】:2015-02-17 20:47:57
【问题描述】:

我在 python 和 C 方面有一定的经验,但对于将 python 模块编写为 C 函数的包装器是新手。对于一个项目,我需要一个名为“score”的函数,它的运行速度比我在 python 中的运行速度要快得多,所以我用 C 编写了它,实际上只是希望能够从 python 调用它。它接受一个整数的python列表,我希望C函数获取一个整数数组,该数组的长度,然后将一个整数返回给python。这是我当前(工作)的解决方案。

static PyObject *module_score(PyObject *self, PyObject *args) {
    int i, size, value, *gene;
    PyObject *seq, *data;

    /* Parse the input tuple */
    if (!PyArg_ParseTuple(args, "O", &data))
        return NULL;
    seq = PySequence_Fast(data, "expected a sequence");
    size = PySequence_Size(seq);

    gene = (int*) PyMem_Malloc(size * sizeof(int));
    for (i = 0; i < size; i++)
        gene[i] = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i));

    /* Call the external C function*/
    value = score(gene, size);

    PyMem_Free(gene);

    /* Build the output tuple */
    PyObject *ret = Py_BuildValue("i", value);
    return ret;
}

这可行,但似乎以我无法忽视的速度泄漏内存。我通过暂时使 score 函数返回 0 并仍然看到泄漏行为来确保在显示的函数中发生泄漏。我曾认为对 PyMem_Free 的调用应该处理 PyMem_Malloc 的存储,但我目前的猜测是,这个函数中的某些东西在每次调用时都会被分配和保留,因为泄漏行为与调用这个函数的次数成正比。我是否没有正确执行序列到数组的转换,或者我是否可能低效地返回结束值?任何帮助表示赞赏。

【问题讨论】:

  • 它认为,Python 有一个内存池,调用PyMem_Free 不会立即释放指针。它将在内部释放它,以便 Python 可以重用它而无需再次分配它。但是,我不确定。
  • 你在 Linux 上吗?你是如何确定有泄漏的?
  • 我在 Windows 上使用 cygwin,这是一个类似 Linux 的环境。我使用 windows 任务管理器和 top 来查看 python 进程消耗越来越多的内存,然后随机等待打印,以查看内存仅在调用此函数而不是其他 python 函数时才增长。
  • 我会推荐一个memory debugging tool,对于Linux,有一个很棒的叫valgrind,你可以在网上搜索任何类似的Windows 工具。您发布的功能,至少在我看来,一点也不错。
  • 请注意,您确实有一个问题,即您将内存分配为sizeof(int)*size,但您使用longs 而不是ints 填充它。

标签: python c++ c memory-leaks python-module


【解决方案1】:

seq 是一个新的 Python 对象,因此您需要删除该对象。你也应该检查seq 是否为NULL。

类似的东西(未经测试):

static PyObject *module_score(PyObject *self, PyObject *args) {
    int i, size, value, *gene;
    long temp;
    PyObject *seq, *data;

    /* Parse the input tuple */
    if (!PyArg_ParseTuple(args, "O", &data))
        return NULL;
    if (!(seq = PySequence_Fast(data, "expected a sequence")))
        return NULL;

    size = PySequence_Size(seq);

    gene = (int*) PyMem_Malloc(size * sizeof(int));
    for (i = 0; i < size; i++) {
        temp = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i));
        if (temp == -1 && PyErr_Occurred()) {
            Py_DECREF(seq);
            PyErr_SetString(PyExc_ValueError, "an integer value is required");
            return NULL;
        }
        /* Do whatever you need to verify temp will fit in an int */
        gene[i] = (int*)temp;
    }

    /* Call the external C function*/
    value = score(gene, size);

    PyMem_Free(gene);
    Py_DECREF(seq):

    /* Build the output tuple */
    PyObject *ret = Py_BuildValue("i", value);
    return ret;
}

【讨论】:

  • 对不起,我是新手...如何删除 seq 对象?我在您的示例中没有看到您删除它的任何地方。它不应该被释放,除非它是用 calloc 或 malloc 创建的,对吧?
  • 我更新了答案。要删除 Python 对象,请使用 Py_DECREF()。我还检查了PyInt_AsLong() 的返回值,并在转换为 int 之前添加了一个验证值大小的位置。
  • 谢谢,就是这样!现在不再有内存泄漏。
猜你喜欢
  • 2021-03-30
  • 2017-07-25
  • 2015-12-12
  • 2019-07-16
  • 2021-07-10
  • 2012-01-18
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
相关资源
最近更新 更多