【问题标题】:Avoiding memory leaks when storing OpenCV Mat objects in STL containers在 STL 容器中存储 OpenCV Mat 对象时避免内存泄漏
【发布时间】:2013-11-01 20:39:23
【问题描述】:

我正在使用 OpenCV,我想在一个向量中存储许多图像(Mat 对象)。我已将向量声明如下以存储指向Mat 对象的指针。

std::vector<Mat*> images;

Mat 对象是使用 new 关键字创建的,然后添加到向量中。

Mat *img = new Mat(height, width, CV_8UC3);
// set the values for the pixels here
images.push_back(img);

如何确保释放Mat 对象占用的内存以避免内存泄漏?

我现在正在做的事情如下:

Mat *im = images.at(index);
// process and display image here
delete(im);

Valgrind 正在报告可能的内存泄漏,参考创建的 Mat 对象。我错过了什么吗?

编辑:

好的。显然最好避免使用Mat 指针并使用new 动态分配Mat。我已修改我的代码以使用 std::vector&lt;Mat&gt; 代替。但是,我仍然看到一些由Mat 分配的块可能在 Valgrind 报告中丢失了。我还注意到程序运行时内存使用量稳步增加。

让我澄清一下我在做什么。我在函数中创建图像并将它们放在缓冲区中(内部使用std::deque)。这个缓冲区然后被另一个函数访问以检索和图像并将其传递给另一个执行处理和渲染的函数。

class Render {
   public:
      void setImage(Mat& img) {
         this->image = img;
      }

      void render() {
         // process image and render here
      }
   private:
      Mat image;
}

不断从缓冲区获取图像并渲染它们的线程。

void* process(void *opaque) {

   ImageProcessor *imgProc = (ImageProcessor*) opaque;
   Mat img;


   while (imgProc->isRunning) {
      // get an image from the buffer
      imgProc->buffer->getFront(img);

      // set the image
      imgProc->renderer->setImage(img);

      // process and render
      imgProc->renderer->render();
   }

}

现在,所有内容都作为对象引用传递(即Mat&amp;)。我假设从缓冲区获取图像并将其传递给渲染函数后,对该对象的唯一引用将在该函数中。因此,当我得到另一个图像时,将不再有对该对象的引用,它将被销毁。但是 Valgrind 给了我以下信息:

25,952,564 bytes in 11 blocks are possibly lost in loss record 14,852 of 14,853
  in ImageProcessor::generateImage() in ImageProcessor.cpp:393
  1: malloc in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so
  2: cv::fastMalloc(unsigned long) in /usr/local/lib/libopencv_core.so.2.4.2
  3: cv::Mat::create(int, int const*, int) in /usr/local/lib/libopencv_core.so.2.4.2
  4: cv::Mat::create(int, int, int) in /usr/local/include/opencv2/core/mat.hpp:353
  5: cv::Mat::Mat(int, int, int) in /usr/local/include/opencv2/core/mat.hpp:75
  ...

这里是generateImage()

void generateImage() {
   Mat img(h, w, CV_8UC3); 
   // set values of pixels here
   this->buffer->pushBack(img);
}

【问题讨论】:

  • Mat 类管理内存分配并具有引用计数,因此std::vector&lt;Mat&gt; 为您完成工作,并且仍然只复制指针和大小信息,而没有不必要的分配和大型 Matrix 内容的副本。跨度>
  • @mars 在按照您的建议更新问题后,我已经用代码的某些部分更新了问题。虽然我仍然遇到内存泄漏,但问题仍然存在。
  • 这里显示的代码部分应该可以工作。你能详细说明一下那个不透明的指针吗?它来自哪里,它是如何被初始化的,谁拥有它并在不再需要它时调用ImageProcessor 的析构函数?注意void * opaque=new ImageProcessor; do_stuff(opaque); free opaque;调用ImageProcessor的构造函数,但调用void的析构函数。
  • @mars 不透明指针基本上是指向ImageProcessor 对象的指针。正如我提到的process() 是一个线程函数。我使用 pthreads 库创建了一个线程,并传递了一个指向 process() 的指针和一个指向基本上是线程函数参数的对象的指针。这就是使用pthread_create() 创建线程的方式。在这里,我只是传递了一个对ImageProcessor 的引用,以便线程可以调用它的方法。何时何地调用ImageProcessor 的析构函数应该不是问题。因为我要从缓冲区中删除 Mat 对象。

标签: c++ opencv memory-leaks


【解决方案1】:

这似乎是共享指针的一个很好的用例。智能指针背后的基本思想是它的行为类似于引用计数的常规指针。当没有其他东西持有对它的引用时,它会自动释放自己。

您在示例中使用它们的方式是

std::vector<std::shared_ptr<Mat> > images;
std::shared_ptr<Mat> mat_ptr (new Mat(height, width, CV_8UC3));
images.push_back(mat_ptr);

Here 是一个方便的教程,了解更多关于共享指针的信息。如果我对您的问题/问题有误解,请随时在下方跟进。

【讨论】:

    【解决方案2】:

    如果您小心地实际删除对象,则不会出现内存泄漏。话虽如此,有一些方法可以将指针存储在不太容易出现人为错误的向量中(即,您忘记删除应用程序某些极端情况下的对象)。

    如果您可以使用 C++11,那么我建议您使用 std::shared_ptr 而不是原始指针。这将确保在不再有任何代码使用您的 Mat 对象时(自动)删除内存。

    还有一个好处是,除了必须更改指针的类型之外,您无需进行任何其他修改。通常的ptr-&gt;member*ptr 表达式也适用于std::shared_ptr

    这里有一些documentation on std::shared_ptr。此外,根据您的具体需求,您可能还想查看std::reference_wrapper


    编辑:由于 C++11 不是一个选项,您可以尝试 boost::shared_ptr — 标准 std::shared_ptr 是基于 Boost 的。如果 Boost 也不是一个选项,你可以实现自己的共享指针,这不是很难。

    您的程序的大小是另外一个需要考虑的问题。如果您的应用程序相当小,并且您几乎没有机会忘记一些可能导致内存泄漏的极端情况,那么所有这些都可能是过度设计的。如果简单地“手动”删除对象就可以考虑这样做。

    【讨论】:

    • 不幸的是,迁移到 C++11 可能并不简单。有没有办法在不使用 C++11 的情况下做到这一点?我的意思是,释放使用 new 关键字创建的 Mat 对象分配的内存的正确方法是什么?我知道创建一个没有new 的对象应该在没有任何引用时释放所有资源。但我不确定使用new 动态创建它。
    • 如果一个对象是用new创建的,你必须(在某个时候)使用delete销毁它。请参阅我对 C++11 替代方案的编辑。
    • 我已更改代码以避免使用指针以及手动分配和删除对象。我编辑了问题以提供更多详细信息。但显然,我仍然面临同样的问题。
    猜你喜欢
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-17
    • 2023-03-10
    • 2018-04-08
    相关资源
    最近更新 更多