我应该将Py_INCREF用于此块中的任何PyObject吗?我也正确地Py_DECREFing我的对象?

时间:2018-04-26 21:37:04

标签: python c++ python-c-api reference-counting

每当我调用此函数时,每次调用的内存使用量都会增加很多,所以我认为这里有一些内存泄漏。

PyObject *pScript, *pModule, *pFunc, *pValue;
PyObject *pArgs = NULL;
long ret = 1;

// Initialize python, set system path and load the module
pScript = SetPyObjectString(PYTHON_SCRIPT_NAME);
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('"PYTHON_SCRIPT_PATH"')");
pModule = PyImport_Import(pScript);
Py_XDECREF(pScript);

if (pModule != NULL) {
    // Get function object from python module
    pFunc = PyObject_GetAttrString(pModule, operation.c_str());

    if (pFunc && PyCallable_Check(pFunc)) {
        // Create argument(s) as Python tuples
        if (operation == UPDATE_KEY) {
            // If operation is Update key, create two arguments - key and value
            pArgs = PyTuple_New(2);
        }
        else {
            pArgs = PyTuple_New(1);
        }

        pValue = SetPyObjectString(key.c_str());
        // Set argument(s) with key/value strings
        PyTuple_SetItem(pArgs, 0, pValue);
        if (operation == UPDATE_KEY) {
            // If operation is Update key, set two arguments - key and value
            pValue = SetPyObjectString(value.c_str());
            PyTuple_SetItem(pArgs, 1, pValue);
        }

        // Call the function using function object and arguments
        pValue = PyObject_CallObject(pFunc, pArgs);
        Py_XDECREF(pArgs);

        if (pValue != NULL) {
            // Parse the return values
            ret = PyLong_AsLong(PyList_GetItem(pValue, 0));
            value = GetPyObjectString(PyList_GetItem(pValue, 1));
        }
        else {
            ERROR("Function call to %s failed", operation.c_str());
        }

        Py_XDECREF(pValue);
        Py_XDECREF(pFunc);
    }
    else {
        ERROR("Cannot find function in python module");
    }
    Py_XDECREF(pModule);
}
else {
    ERROR("Failed to load python module");
}

当我的代码中的这个C ++代码段调用python脚本并且我想知道原因时,我正在泄漏一些内存。我想我的Py_DECREFs做错了。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

我从快速的目光中发现了一个失踪的减压:

pFunc = PyObject_GetAttrString(pModule, operation.c_str());

if (pFunc && PyCallable_Check(pFunc)) {
    // ...
    Py_XDECREF(pFunc);
}

这将泄漏与operation匹配的任何不可调用属性。

pValue的两次重新分配...我认为没关系,因为PyTuple_SetItem窃取了对每个原始值的引用。

对于您询问的这一行:

value = GetPyObjectString(PyList_GetItem(pValue, 1));

PyList_GetItem返回一个借来的引用,所以你没有减去它的事实是正确的。

但我没有看到value在任何地方或GetPyObjectString的声明,所以我不知道那部分是做什么的。也许它只是从PyUnicodeObject *中获取一个借来的缓冲区并将其复制到某个C ++ wstring或UTF-32字符串类型中,或者它可能泄漏了Python对象或复制的缓冲区,或者返回了原始C你刚刚在这里泄漏的缓冲区,或者......谁知道?

但我当然不相信互联网上的某些人会在快速扫描中发现所有这些人。学习使用内存调试器。

或者:你正在使用C ++。 RAII几乎是使用C ++的重点 - 换句话说,不是使用原始PyObject *值,而是可以使用智能指针自动为您量身定制。或者,更好的是,使用像PyCXX这样的现成库。