我写的Python一个C ++模块。 它需要一个形象,做了一些处理,并返回图像的字典。 我有内存泄漏问题,我想不通为什么..
我使用OpenCV的-ndarray转换之间进行转换cv::Mat
和numpy.ndarray
我用的Boost.Python到C ++代码转换为Python模块。
我用下面的Python代码来测试C ++模块,在运行htop
检查内存使用情况。
import cv2
import this_cpp_module
for i in xrange(100000):
img = cv2.imread('a_640x480x3_image.png')
ret = this_cpp_module.func(img)
#this 'func' is mapping to one of the following c++ functions, using Boost.Python:
# func1, func2 or func3.
如图1所示,转换图像不导致内存泄漏
using namespace boost::python;
PyObject * func1(PyObject *image)
{
NDArrayConverter cvt;
cv::Mat mat;
mat = cvt.toMat(image);
PyObject* ret = cvt.toNDArray(mat);
return ret;
}
2,构建字典,把图像转换成它不会导致内存泄漏
using namespace boost::python;
dict func2(PyObject *image)
{
dict pyDict;
object objImage(handle<>(borrowed(image)));
pyDict[std::string("key")] = objImage;
return pyDict;
}
3,但将它们组合使内存泄漏(大约每循环1MB)
dict func3(PyObject *image)
{
return func2(func1(image));
}
我想不明白。 一切似乎是正确的我,但结合在一起只是导致此问题。
该泄漏的结果func3()
从来没有正确处理返回的临时拥有的引用func1()
。 为了解决这个问题, func3()
需要做以下之一:
- 显式调用
Py_DECREF()
从返回的拥有的引用func1()
从返回之前func3()
- 管理返回的值
func1()
用boost::python::handle
,因为它会递减对象的引用计数,当handle
被破坏。
例如, func3()
可以被写为:
boost::python::dict func3(PyObject* image)
{
// func1() returns an owned reference, so create a handle to keep the
// object alive for at least as long as the handle remains alive. The
// handle will properly dispose of the reference.
boost::python::handle<> handle(func1(image));
return func2(handle.get());
}
有关原始问题的细节,当func1()
返回,返回的对象具有的引用计数1
。 在从返回func2()
和func3()
该物体具有一个参考计数2
。 当dict
从返回func3()
被破坏时,该对象最初从返回func1()
将具有其引用计数递减由1
,导致泄漏的对象具有引用计数1
。
这是在原有基础上的代码一个完整的小例子:
#include <boost/python.hpp>
PyObject* func1(PyObject*)
{
return PyList_New(0);
}
boost::python::dict func2(PyObject* obj)
{
namespace python = boost::python;
python::dict dict;
python::handle<> handle(python::borrowed(obj));
dict[std::string("key")] = python::object(handle);
return dict;
}
boost::python::dict func3(PyObject* obj)
{
// Fails to properly dispose of the owned reference returned by func1(),
// resulting in a leak.
return func2(func1(obj));
}
boost::python::dict func4(PyObject* obj)
{
// func1() returns an owned reference, so create a handle to keep the
// object alive for at least as long as the handle remains alive. The
// handle will properly dispose of the reference.
boost::python::handle<> handle(func1(obj));
return func2(handle.get());
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("func1", &func1);
python::def("func2", &func2);
python::def("func3", &func3);
python::def("func4", &func4);
}
互动用法:
>>> from sys import getrefcount
>>> import example
>>> x = example.func1(None)
>>> assert(2 == getrefcount(x)) # refs: x and getrefcount
>>> d = example.func2(x)
>>> assert(3 == getrefcount(x)) # refs: x, d["key"], and getrefcount
>>> d = None
>>> assert(2 == getrefcount(x)) # refs: x and getrefcount
>>> d = example.func3(None)
>>> x = d["key"]
>>> assert(4 == getrefcount(x)) # refs: x, d["key"], getrefcount, and one leak
>>> d = None
>>> assert(3 == getrefcount(x)) # refs: x, getrefcount, and one leak
>>> d = example.func4(None)
>>> x = d["key"]
>>> assert(3 == getrefcount(x)) # refs: x, d["key"], and getrefcount
>>> d = None
>>> assert(2 == getrefcount(x)) # refs: x and getrefcount