蟒蛇3+;在这个简单的例子中,ctypes回调导致内存泄漏 在使用Python 3代码和C++代码结合CyType的复杂程序上工作时,发现了一个内存泄漏,可以很容易地用下面的示例来复制。 < >我的C++代码使用回调函数创建Python对象。接下来,它调用Python对象上的另一个回调,该回调只返回其参数。第二次回调导致对象的引用计数增加。因此,对象永远不会被垃圾收集
这是Python代码(文件bug.py):蟒蛇3+;在这个简单的例子中,ctypes回调导致内存泄漏 在使用Python 3代码和C++代码结合CyType的复杂程序上工作时,发现了一个内存泄漏,可以很容易地用下面的示例来复制。 < >我的C++代码使用回调函数创建Python对象。接下来,它调用Python对象上的另一个回调,该回调只返回其参数。第二次回调导致对象的引用计数增加。因此,对象永远不会被垃圾收集,python,c++,memory-leaks,garbage-collection,ctypes,Python,C++,Memory Leaks,Garbage Collection,Ctypes,这是Python代码(文件bug.py): 这是C++代码(文件bug.CPP): 输出为: ref cnt = 1 ref cnt = 2 ref cnt = 1 ref cnt = 1 garbage collect foo 换句话说,对noop函数的调用错误地增加了引用计数,并且Foo对象没有被垃圾收集。如果不调用noop函数,Foo对象将被垃圾收集。预期产出为: ref cnt = 1 ref cnt = 2 ref cnt = 1 ref cnt = 1 garbage coll
这是C++代码(文件bug.CPP):
输出为:ref cnt = 1
ref cnt = 2
ref cnt = 1
ref cnt = 1
garbage collect foo
换句话说,对noop函数的调用错误地增加了引用计数,并且Foo对象没有被垃圾收集。如果不调用noop函数,Foo对象将被垃圾收集。预期产出为:
ref cnt = 1
ref cnt = 2
ref cnt = 1
ref cnt = 1
garbage collect foo
这是一个已知的问题吗?有人知道变通方法或解决方案吗?这是由ctypes中的错误引起的吗?您正在传递Python对象。您的一个对象被传递到您的C代码中,而不是传递出去,因此您要负责引用计数。下面是一些有效的方法,但我已将
void*
更改为PyObject*
,因为它们就是这样的:
#include <Python.h>
#include <iostream>
#include <assert.h>
extern "C" {
typedef PyObject* (*CreateObjectCallback)();
typedef PyObject* (*NoopCallback)(PyObject* arg);
__declspec(dllexport) PyObject* test(CreateObjectCallback create, NoopCallback noop)
{
// Create the object, with one reference.
PyObject* object = create();
std::cerr << "ref cnt = " << object->ob_refcnt << std::endl;
// Passing object back to Python increments its reference count
// because the parameter of the function is a new reference.
// That python function returns an object (the same one), but
// now you own deleting the reference.
PyObject* object2 = noop(object);
Py_DECREF(object2);
std::cerr << "ref cnt = " << object->ob_refcnt << std::endl;
// Your return the created object, but now that Python knows
// it is a Python object instead of void*, it will decref it.
return object;
}
}
输出:
ref cnt = 1
ref cnt = 1
garbage collect foo
这正是我一直在寻找的答案,甚至更多关于PyDLL和decorators的问题;非常感谢。是否有任何关于ctypes.py_对象如何处理引用计数的公共文档?例如,我并不清楚将Python对象作为参数传递给回调会永久增加引用计数;我希望从回调返回会再次减少引用计数。@ygramoel
lib.test()
创建了一个对象(ref1),并且noop
被传递给了该对象(ref2…参数名引用了它)noop
将ref返回给调用方。调用方负责在处理完对象后取消引用该对象noop
是由lib.test
从C调用的,因此lib.test
必须减小它lib.test
将它创建的对象返回给Python。Python代码没有将其分配给变量,因此Python减少了引用计数。如果Python代码将其分配给一个变量,它将保持引用计数,并且Foo
不会被垃圾收集。@ygramoel。
import ctypes
CreateObjectCallback = ctypes.CFUNCTYPE( ctypes.py_object )
NoopCallback = ctypes.CFUNCTYPE( ctypes.py_object, ctypes.py_object )
lib = ctypes.PyDLL('test')
lib.test.restype = ctypes.py_object
lib.test.argtypes = [ CreateObjectCallback, NoopCallback ]
class Foo:
def __del__(self):
print("garbage collect foo");
@CreateObjectCallback
def create():
return Foo()
@NoopCallback
def noop(object):
return object
lib.test(create,noop)
ref cnt = 1
ref cnt = 1
garbage collect foo