【问题标题】:PyObject_CallMethod leaking / seg faultPyObject_CallMethod 泄漏/段错误
【发布时间】:2018-11-25 10:48:11
【问题描述】:

我从 C 调用 Python 方法,10 小时后 PyObject_CallMethod 发生泄漏/段错误。

您能查看我的代码吗?我的错误在哪里?缺少什么?

C 代码:

PyObject *pFrameAsList = PyList_New(0);
...
for (int k=0; k<m_nbImages; ++k)
{
   PyObject *pFrameAsArray = toNDArray(frame);
   ...
   PyList_Append(pFrameAsList, pFrameAsArray);
   Py_DECREF(pFrameAsArray);
}
PyObject *pValue = PyObject_CallMethod(pInstance, "analyseFrame", "(O)", pFrameAsList);
...
Py_INCREF(pValue);
...
for (int k=0 ; k<max; k++ )
{
    PyObject *ptemp = PyList_GetItem(pValue,k);
}
Py_DECREF(pFrameAsList);
Py_DECREF(pValue);

还有 Python 代码:

def analyseFrame(self, frame):
   results = self.model.analyse(np.asarray(frame))
   return list(results)

【问题讨论】:

  • 您缺少到处都是错误检查!它们不是 C-API 的“可选”...
  • 即您必须检查PyList_NewtoNDArrayPyList_AppendPyObject_CallMethod的返回值以及max的返回值PyList_GetItem;在这种情况下,可能是您的代码没有崩溃,但会以 MemoryError 退出。
  • 你没关系,检查返回值/代码是最佳实践,它在我们的代码中完成,但我删除了这里的检查以减少代码。
  • 当您没有显示这些错误检查时,我们如何验证它们是否正确!

标签: python c memory-leaks segmentation-fault


【解决方案1】:

我认为问题在于您不应该有以下行:

Py_INCREF(pValue)

原因是PyObject_CallMethod 已经为您增加了引用计数。如果您执行额外的Py_INCREF 调用,则给定PyObject 的引用计数将永远不会变为0。

根据文档,PyObject_CallMethod returns a new reference

要了解 “新参考” 的含义,而不是 “借用参考”,请查看文档Reference Count Details 部分的第一段:

【讨论】:

  • 如果我删除 Py_INCREF(pValue),我会直接 seg 错误。谢谢你的链接,我会读的。
  • 我根据您的评论和链接根据新的/借用的参考资料进行了审查。我删除了所有返回借用引用的方法上的 Py_INCREF。我正在测试...
  • 根据文档,调用者必须对 PyObject_CallMethod 的返回值执行 Py_INCREF 对我来说似乎不正确,因为返回的引用被认为是“新的”,而不是“借来的”。也许您将 pValue 作为参数传递给某个“窃取”引用的函数? “窃取”引用的概念记录在我在答案中发布的相同引用计数详细信息链接中。
  • 按照stackoverflow.com/questions/510406/… 中的建议,您还可以在代码中的各个点使用 sys.getrefcount(pValue) 来查看代码中各个位置的当前引用计数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-16
  • 2012-01-30
相关资源
最近更新 更多