2014-07-21 44 views
1

我正在爲python寫一個C++模塊。它拍攝一張圖像,做一些處理並返回圖像字典。我有內存泄漏問題,我想不通爲什麼..返回ndarray字典導致內存泄漏使用boost python

我用opencv-ndarray-conversion轉換cv::Matnumpy.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)); 
} 

我無法弄清楚。一切似乎都對我來說是正確的,但將它們結合在一起只會導致這個問題。

+0

你能添加源'func',可能涉及到內存泄漏。 –

+0

@AndrewJohnson'func'映射到一個C++函數:'func1','func2'或'func3'。 – 01zhou

回答

2

泄漏是由於func3()從未妥善處置由func1()返回的臨時擁有的參考。爲了解決這個問題,func3()需要做以下之一:

  • func3()返回前顯式調用從func1()返回資參考Py_DECREF()
  • 管理由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()返回,返回的對象有一個reference count of 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 
+0

非常感謝! – 01zhou