【问题标题】:C-extension in Python - pyObject called Py_DECREF ,reference is 0,but memory leakPython 中的 C 扩展 - 名为 Py_DECREF 的 pyObject,引用为 0,但内存泄漏
【发布时间】:2021-03-01 07:50:38
【问题描述】:

这是我的代码。

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),这样可以释放内存。这很困扰我。

【问题讨论】:

  • 你如何测量内存?你如何检查“内存不是空闲的”?
  • 认为您可以将内部循环替换为PyList_SetItem(pyParams, Py_BuildValue(...)),因为它将获取引用。
  • 我在windows任务管理器里看到了。
  • 这不可靠或不足以衡量实际的内存泄漏。尝试找到一些内存分析工具来帮助您衡量实际分配和空闲。
  • @AaronD.Marasco:这仅在 list 已预先确定大小并且您正在填写现有但未填充的索引时才有效。他们使用了PyList_New(0),这就排除了这一点。

标签: python c python-extensions


【解决方案1】:

Python(和大多数语言)不直接满足操作系统的内存分配,也不会在发布时立即返回分配。它批量分配内存,并通过对块进行分区来满足较小的请求。即使一个块中的所有内存都被“释放”了,它并不总是返回给操作系统,而是保留以备将来分配。

在这种情况下,您正在创建 floatlist 对象,它们都有自己的“空闲列表”,因此释放不会实际上将它们返回给分配器,但是对于一个简单的已分配但未使用的对象堆栈,floatlist 构造函数可以从中提取比向分配器请求更多内存更便宜。问题是,这也意味着这些元素所在的块根本无法返回给操作系统,因为它们的一部分仍然被分配,至少从分配器的角度来看是这样。您可以通过显式调用PyGC_Collect()(仅在a What's New doc 中记录的副作用)来清除这些空闲列表,这可能允许内存返回给操作系统,但同样不能保证。您可能还会want to disable pymalloc (以避免额外的小对象领域更不可能被交还给操作系统)。尽管如此,Python 完全有权无限期地保留大部分内存以满足未来的分配。

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

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

【讨论】:

  • 出现了一个新问题,调用PyGC_Collect()需要十多秒。python2.7
  • @KeyboardMan:Python 2.7?为什么?!?它已经结束生命并且完全不受支持近一年了。你为什么要为它编写新代码,更不用说新的扩展代码(它的可移植性明显低于 Python 级代码)? 10 秒确实看起来异常长,但没有办法知道你做了什么导致它。无论如何,普通代码在正常情况下不应该调用PyGC_Collect();它会间歇性地分阶段自行运行,几乎不需要强制它,我只是提到它是一种强制清除空闲列表的方法(主要目的的副作用)。
猜你喜欢
  • 2019-07-16
  • 1970-01-01
  • 2010-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2018-04-08
  • 2014-03-10
相关资源
最近更新 更多