近日在写一段opencv程序,使用cvGetSubRect()函数时碰到了内存泄漏问题(参见:Justin )。本人菜鸟,对这个所谓的内存泄漏问题一知半解,碰到了才想到去弄明白。


    首先google + wikipedia,它们告诉我所谓 内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。


    那么,怎样才会“在释放该内存前就失去了对该内存的控制呢”?小菜立刻写了这样一段c++代码:




1. //产生内存泄漏的一段代码  
2. int
3. {

4. char
5. for (int
6.     {

7. new char[100000]; //只有new,没有delete,将产生内存泄漏  
8. //延时100ms,可以打开任务管理器观测内存变化  
9.     }
10. return
11. }

    果然,程序运行后,打开任务管理器,发现内存使用在狂飙上升。对照上面的定义,分析:第一次new之后,将分配的地址赋给了指针str,此时,我们对改内存空间是有控制的的;第二次new之后,又分配了一块同样大小的内存(注意此时第一次分配的内存并未释放),当指针str重新赋值后,此时str指向第二次分配的内存,我们就丢失对第一次分配的内存的控制了,内存泄漏就产生了。程序不断执行下去,没有得到释放的内存片段越来越多,直到内存耗尽程序崩溃。

    因此,动态分配的内存在不适用时一定要释放。比如上面的代码,可改为:


       好了,到此,小菜对内存泄漏总算是有个直观的印象了。下面琢磨下cvGetSubRect()函数是怎样产生内存泄漏的。函数原型:

1. int
2. {

3. char
4. for (int
5.     {

6. new char[100000]; //只有new,没有delete,将产生内存泄漏  
7. //延时100ms,可以打开任务管理器观测内存变化  
8. delete []str;           //释放内存  
9.     }
10. return
11. }



1. CV_IMPL  CvMat*
2. cvGetSubRect( const


    小菜原来的用法:

1. ...
2. 
3. CvMat *firstFrameSub = cvCreateMat( rows, cols, CV_8UC1 ); //创建一个rows*cols 的8-bit 无符号单通道矩阵 
4. 
5. cvGetSubRect(src, firstFrameSub, rect); //此处产生内存泄漏! 
6. 
7. ...

    进入cvGetSubRect定义代码:


1. CV_IMPL  CvMat*
2. cvGetSubRect( const
3. {

4.     CvMat* res = 0;
5.     
6. "cvGetRect"
7. 
8.     __BEGIN__;
9. 
10. 
11.     CvMat stub, *mat = (CvMat*)arr;
12. 
13. if( !CV_IS_MAT( mat ))
14.         CV_CALL( mat = cvGetMat( mat, &stub ));
15. 
16. if( !submat )
17. ""
18. 
19. if( (rect.x|rect.y|rect.width|rect.height) < 0 )
20. ""
21. 
22. if( rect.x + rect.width > mat->cols ||
23.         rect.y + rect.height > mat->rows )
24. ""
25. 
26.     {

27. //从此处可以看出,如果submat已经分配有内存空间,那么,此处将产生内存泄漏,切记传递submat时,一定要穿一个没有分配内存的指针。  
28. size_t)rect.y*mat->step +
29.                        rect.x*CV_ELEM_SIZE(mat->type);
30.     submat->step = mat->step & (rect.height > 1 ? -1 : 0);
31.     submat->type = (mat->type & (rect.width < mat->cols ? ~CV_MAT_CONT_FLAG : -1)) |
32.                    (submat->step == 0 ? CV_MAT_CONT_FLAG : 0);
33.     submat->rows = rect.height;
34.     submat->cols = rect.width;
35.     submat->refcount = 0;
36.     res = submat;
37.     }
38.     
39.     __END__;
40. 
41. return
42. }


    因此,使用cvGetSubRect时候要非常注意,它的第二个参数不能预先分配内存。这和大部分的opencv函数不一样。