【发布时间】:2015-09-14 04:33:23
【问题描述】:
我目前正在使用 Cython 包装一个库。为此,我想重用纯 C 绑定的一个函数。
这是基本设置:
- mylib.pxd
- old_lib.c
- old_lib.h
在 mylib.pxd 我做:
cdef extern from old_lib.h:
PyObject* get_pyobject()
然后将old_lib.c 作为我的扩展中的源文件传递:
setup(ext_modules=[Extension("mylib", sources=["mylib.pxd", "old_lib.c"])])
在 mylib.pxd 中,我使用 get_pyobject 创建一个我想返回的新对象,如下所示:
cdef PyObject* ptr
ptr = get_pyobject()
return <object>ptr
这给了我想要的行为,但我担心这会泄露 ptr 引用。
会吗?我很困惑,因为我发现(旧)引用说你应该自己管理 PyObject* 引用并相应地调用 Py_INCREF/DECREF 但似乎在 Cython FAQ 他们说:
请注意,对象的生命周期仅绑定到其拥有的引用,而不绑定到碰巧指向它的任何 C 指针。
这是否意味着每当返回值被丢弃时,ptr 就会被垃圾回收?
old_lib.c 中的流程是这样的:
PyObject* get_pyobject()
{
PyTypedObject* typeptr = PyObject_NEW_VAR(MyType, &Type, size)
fill_attribute(typeptr->attrib)
return (PyObject*)typeptr
}
其中PyObject_NEW_VAR 在python 标准库(我的版本中为objimpl.h:196)中使用PyObject_InitVar 实现。因此,返回的引用是一个借来的引用,但由于使用了PyObject_MALLOC,我猜这是对该对象的唯一引用。相关代码:
#define PyObject_NEW_VAR(type, typeobj, n) \
( (type *) PyObject_InitVar( \
(PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE((typeobj),(n)) ),\
(typeobj), (n)) )
编辑:
我已经检查过了,当使用上面的代码时,sys.getrefcount 返回 3。据我所知,当我创建对象时,它的引用计数为 1。然后,当将其转换为 object 时,它的引用计数是撞到 2。因此它永远不会被垃圾收集(除非有一种方法可以删除一个只有一个可访问指针的对象的两个引用计数)和泄漏。
如果我插入一个 PY_DECREF,它仍然可以正常工作并正确返回 2。我还花时间直接在 Cython 中重写了该函数,它返回 2。
【问题讨论】:
-
您可以在 Cython 中查找并打印
PyObject的ob_refcnt字段。在得出任何明确结论之前,我会仔细检查它是否设置为您认为应该的(我认为您对没有内存泄漏的代码是错误的,但我不确定 - 看看tiran.bitbucket.org/python-lcov/Objects/object.c.gcov.html (PyObject_InitVar,特别是它所调用的_Py_NewReference)) -
感谢您的洞察力。事实上,我的物品正在泄漏。如果有人能提出一个很好的解释,我很乐意接受!
标签: python c memory-leaks cython