I am writing a c++ module for python. It takes a image, does some processing and returns a dictionary of images. I am having memory leaks which I can't figure out why..
I use opencv-ndarray-conversion to convert between cv::Mat
and numpy.ndarray
I use Boost.Python to convert c++ code to python module.
I use the following python code to test the c++ module, while running htop
to check the memory usage.
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, Converting the image does not cause memory leaks
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, Constructing a dictionary and putting the image into it do not cause memory leaks
using namespace boost::python;
dict func2(PyObject *image)
{
dict pyDict;
object objImage(handle<>(borrowed(image)));
pyDict[std::string("key")] = objImage;
return pyDict;
}
3, But combining them causes the memory leaks (around 1MB per loop)
dict func3(PyObject *image)
{
return func2(func1(image));
}
I cannot figure it out. Everything seems to be correct to me but combining them together just causes this problem.
The leak is a result of
func3()
never properly disposing the temporary owned reference returned byfunc1()
. To resolve this,func3()
needs to do one of the following:Py_DECREF()
on the owned reference returned fromfunc1()
before returning fromfunc3()
.func1()
with aboost::python::handle
, as it will decrement the object's reference count when thehandle
is destroyed.For example,
func3()
could be written as:For details on the original problem, when
func1()
returns, the returned object has a reference count of1
. Upon returning fromfunc2()
andfunc3()
, the object has a reference count of2
. When thedict
returned fromfunc3()
is destroyed, the object initially returned fromfunc1()
will have its reference count decremented by1
, resulting in the leaked object having a reference count of1
.Here is a complete minimal example based on the original code:
Interactive usage: