2015-09-14 13 views
3

我目前正在用Cython包裝一個庫。爲此,我想重新使用純C綁定的一個函數。Cython:返回一個<object> PyObject *,它會泄漏嗎?

這是基本設置:

  • 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.它將永遠不會被垃圾收集(除非有一種方法去除只有一個可訪問指針的對象的兩個refcount)並且泄漏。 如果我插入一個PY_DECREF,它仍然有效並正確返回2.我還需要時間來直接重寫功能在用Cython,並返回2.

+0

您可以查找並打印'PyObject'在用Cython的'ob_refcnt'領域。我會仔細檢查它的設置,因爲你認爲它應該是在得出任何明確的結論之前(我認爲你錯了代碼沒有內存泄漏,但我不確定 - 看看http:/ /tiran.bitbucket.org/python-lcov/Objects/object.c.gcov.html('PyObject_InitVar'特別'_Py_NewReference'它調用)) – DavidW

+0

感謝您的見解。事實上,我的物體在泄漏。如果有人能提出一個很好的解釋,我會很高興接受它! – Urukann

回答

3

old documentationPyObject_NEW_VAR展望是函數PyObject_NewVar的宏版本,(作爲@MadPhysicist說)返回一個「新的參考」(即有一個refcount爲1)。我懷疑你不再鼓勵你使用這個宏,所以它從最近的文檔中消失了。

,它在東西返回「借來參考」方面實現真實的事實應可能只是被看作一個實現細節,而不是這意味着它返回一個「借參考」本身。

關於用Cython行爲,鑄造到<object>遞增引用計數,因此引起了內存泄漏。我的建議診斷它的方法是看引用計數,這樣的事情:

from cpython.ref cimport PyObject # somewhere at the top 

def whatever_function(): 
    cdef PyObject* ptr 
    ptr = get_pyobject() 
    print ptr.ob_refcnt # prints 1 
    ret_val = <object>ptr 
    print ptr.ob_refcnt # prints 2, 
     # but it will only every be decremented to 1, so never be freed 
    return ret_val 

在固定它的條款,你有兩個選擇 - 你可以遞減引用計數一次自己,或者你可以改變用Cython功能的包裝

cdef extern from old_lib.h: 
    object get_pyobject() 

(不要擔心它不完全匹配頭文件!)。 Cython將此解釋爲「get_pyobject()返回一個新的引用,所以我們不會自行增加它,而是從這裏自動處理引用計數。」

+0

謝謝,這正是我一直在尋找的! – Urukann