引子

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()方法

对照一下,你平时有注意过这些不,没有的话从现在开始还来得及的!

滥用/重用变量导致内存泄漏

注意要避免下面三种错误代码写法

错误一:

1Mat m1;
2for (int i = 0; i 100; i++) {
3         m1 = Mat::zeros(Size(512, 512), CV_8UC3);
4}
5imshow("input-m1", m1);
6m1.release();

错误方式:在循环中创建无数Mat对象,结果只释放了一个,很多人的循环就是这么写的,妥妥的内存泄漏!类似的代码操作应该避免。

错误二:

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

错误方式:以为可以少创建一个变量,结果是无法释放内存了,反复调用导致内存泄漏,类似的代码应该避免。

错误三:

有返回Mat对象,但是提前调用release释放了,结果再次访问data数据块,导致程序直接崩溃!需要特别注意!简单演示如下:

1#include 
 2#include 
 3
 4using namespace std;
 5using namespace cv;
 6Mat my_process();
 7int main(int argc, char** argv) {
 8    // 错误三:
 9    Mat image = my_process();
10    imshow("input-image", image);
11    waitKey(0);
12}
13Mat my_process() {
14     Mat m1 = Mat::zeros(Size(512, 512), CV_8UC3);
15     // TODO: do something here
16     m1.release();
17     return m1;
18}


总结


使用C++开发,内存跟指针问题是很多开发者头疼的一件事情,OpenCV框架对内存的管理已经很智能化了,基本上代码规范写,记得release就不会有这个方面的问题,但是还是小心为妙,特别是跨语言调用的时候比如Android/Java通过JNI调用OpenCV函数,如果不注意release或者没有正确release,很难找到原因!