首页 > 解决方案 > 我应该对这个块中的任何 PyObjects 使用 Py_INCREF 吗?我也正确 Py_DECREFing 我的对象吗?

问题描述

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

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_DECREF 做错了什么。任何帮助将非常感激。

标签: pythonc++python-c-apireference-counting

解决方案


我一眼就发现了一个缺失的 deref:


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,所以我不知道那部分在做什么。也许它只是从 a 中获取一个借来的缓冲区PyUnicodeObject *并将其复制到某个 C++wstring或 UTF-32 字符串类型中,或者它可能正在泄漏 Python 对象或复制的缓冲区,或者返回您刚刚在此处泄漏的原始 C 缓冲区,或者......谁知道?


但我当然不相信互联网上的某个人通过快速扫描找到了所有这些。学习使用内存调试器。

或者:您正在使用 C++。RAII 几乎是使用 C++ 的全部要点——换句话说,PyObject *您可以使用智能指针来自动为您定义事物,而不是使用原始值。或者,更好的是,使用像PyCXX这样的现成库。


推荐阅读