Python 中的 C 扩展 - pyObject 称为 Py_DECREF,引用为 0,但内存泄漏

C-extension in Python - pyObject called Py_DECREF ,reference is 0,but memory leak

这是我的代码。

PyObject *dataPyParams = PyList_New(0);
for (int i = 0; i < figdata.dataSetList.size(); i++)
{
    PyObject *pyParams = PyList_New(0);
    for (int j = 0; j < figdata.dataSetList[i].size(); j++)
    {
        //std::cerr << figdata.dataSetList[i][j] << "data\n";
        auto temp = Py_BuildValue("f", figdata.dataSetList[i][j]);
        PyList_Append(pyParams,temp);
        Py_DECREF(temp);    
    }
    PyList_Append(dataPyParams, pyParams);
    Py_DECREF(pyParams);
    
}
Py_DECREF(dataPyParams);

我调用了 Py_DECREF(dataPyParams),dataPyParams 引用为 0,但内存未释放。 我尝试删除 PyList_Append(pyParams,temp),这个释放 memory.It 让我很困扰。

Python(和大多数语言)不直接满足来自 OS 的内存分配,也不会在释放时立即满足 return 分配。它批量分配 内存,并通过划分块来满足较小的请求。即使当一个块中的所有内存都被“释放”时,它并不总是 returned 到 OS,而是保留以备将来分配。

在这种情况下,您正在制作 floatlist 对象,它们都有自己的“空闲列表”,因此实际上 不会释放 return 它们到分配器,但是到一个简单的已分配但未使用的对象堆栈,floatlist 构造函数可以从中提取比请求分配器更便宜的对象更多内存。问题是,这也意味着这些元素所在的块根本不能 returned 到 OS,因为它们的一部分仍然被分配,至少从分配器的角度来看是这样。您可以通过显式调用 PyGC_Collect() 来清除这些空闲列表(仅在 a What's New doc), which might allow memory to be returned to the OS, but again, it's no guarantee. You'd probably also want to disable pymalloc 中由副作用记录(以避免更不可能返回给 OS 的额外小对象区域) ). 尽管如此,Python 完全有权利无限期地保留大部分内存以满足未来的分配。

简而言之,这可能不是内存泄漏。您可以使用更高级的内存分析工具(如果没有别的,如果您在启动前在您的环境中定义 PYTHONMALLOCSTATS=1,Python 本身会告诉您有关竞技场的使用情况),但任务管理器只能看到什么OS 看到,而不是内部内存管理 Python 本身层叠在原始、批量 OS 内存分配之上。

在没有外部工具的情况下查看是否存在泄漏的简单测试是多次 运行 这个确切的代码(包括清理)。如果内存在每次 运行 时都以固定的大数量增加,那么是的,这可能是泄漏。但更有可能的是,您会发现第一个 运行 消耗大量内存,但随后的 运行 增加很少或没有内存使用(由于分配顺序和分配对齐问题,有些可能会被使用,但它会非常小),因为他们正在利用释放的内存(在用户区,而不是 OS)而不是向 OS 请求更多内存。