OpenCV開發中的內存管理問題

  • 2020 年 2 月 12 日
  • 筆記

引子

2020年第二篇技術文章,最近比較忙,事情比較多,搞了一個新的系列技術文章,還沒有完整的搞好,抽空寫一篇最近別人問我的事情!

概述

如果你是OpenCV框架做開發、特別是用OpenCV C++版本或者Java/Android版本JNI的調用的化,可能很多時候你遇到最棘手的問題就是程序運行會越來越慢,甚至死機了,原因很簡單,有時候你有內存泄漏問題。做好下面幾件事情會幫助你在開發中經可能的避免OpenCV對象內存泄漏陷阱。

記得調用release

OpenCV中很多數據結構與對象都有一個release方法,記得用完這些對象跟數據容器之後調用release/destory方法。最典型的就是Mat對象的release方法,調用release並不會重根本上保證立刻回收內存,它只是讓對象的引用計數減一,只有當對象的引用計數為0的時候,才會回收內存。此外release方法還是一個原子操作,也可以線程中被調用。這些方法對象列表如下:

  • Mat的release方法,表示釋放圖像內存
  • FileStorage的releaseAndGetString方法,表示關閉文件,釋放所有內存
  • Mat繼承類/封裝類,UMat、SparseMat等都調用release方法
  • VideoCapture/VideoWriter方法,調用release釋放緩衝區與資源
  • CUDA相關的調用releaseMemory方法,比如SURF_CUDA
  • rgbd相關的調用releasePyramids方法
  • 使用allocate分配內存的必須調用free()方法

對照一下,你平時有注意過這些不,沒有的話從現在開始還來得及的!

濫用/重用變量導致內存泄漏

注意要避免下面三種錯誤代碼寫法

錯誤一:

Mat m1;  for (int i = 0; i < 100; i++) {           m1 = Mat::zeros(Size(512, 512), CV_8UC3);  }  imshow("input-m1", m1);  m1.release();

錯誤方式:在循環中創建無數Mat對象,結果只釋放了一個,很多人的循環就是這麼寫的,妥妥的內存泄漏!類似的代碼操作應該避免。

錯誤二:

Mat image = imread("D:/images/test.png");  image = getProcessed();  imshow("input-image", image);

錯誤方式:以為可以少創建一個變量,結果是無法釋放內存了,反覆調用導致內存泄漏,類似的代碼應該避免。

錯誤三:

有返回Mat對象,但是提前調用release釋放了,結果再次訪問data數據塊,導致程序直接崩潰!需要特別注意!簡單演示如下:

#include <opencv2/opencv.hpp>  #include <iostream>    using namespace std;  using namespace cv;  Mat my_process();  int main(int argc, char** argv) {      // 錯誤三:      Mat image = my_process();      imshow("input-image", image);      waitKey(0);  }  Mat my_process() {       Mat m1 = Mat::zeros(Size(512, 512), CV_8UC3);       // TODO: do something here       m1.release();       return m1;  }

總結

使用C++開發,內存跟指針問題是很多開發者頭疼的一件事情,OpenCV框架對內存的管理已經很智能化了,基本上代碼規範寫,記得release就不會有這個方面的問題,但是還是小心為妙,特別是跨語言調用的時候比如Android/Java通過JNI調用OpenCV函數,如果不注意release或者沒有正確release,很難找到原因!