近日在寫一段opencv程式,使用cvGetSubRect()函數時碰到了記憶體流失問題(參見:Justin http://blog.csdn.net/Justin4wd/archive/2008/06/26/2588243.aspx)。本人菜鳥,對這個所謂的記憶體流失問題一知半解,碰到了才想到去弄明白。 首先google + wikipedia,它們告訴我所謂
記憶體流失指由於疏忽或錯誤造成程式未能釋放已經不再使用記憶體的情況。記憶體流失並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,導致在釋放該段記憶體之前就失去了對該段記憶體的控制,從而造成了記憶體的浪費。 那麼,怎樣才會“在釋放該記憶體前就失去了對該記憶體的控制呢”?小菜立刻寫了這樣一段c++代碼:
- //產生記憶體流失的一段代碼
- int main()
- {
- char *str = NULL;
- for (int i = 0; i < 1000; i++)
- {
- str = new char[100000]; //只有new,沒有delete,將產生記憶體流失
- Sleep(100); //延時100ms,可以開啟工作管理員觀測記憶體變化
- }
- return 0;
- }
果然,程式運行後,開啟工作管理員,發現記憶體使用量在狂飆上升。對照上面的定義,分析:第一次new之後,將分配的地址賦給了指標str,此時,我們對改記憶體空間是有控制的的;第二次new之後,又分配了一塊同樣大小的記憶體(注意此時第一次分配的記憶體並未釋放),當指標str重新賦值後,此時str指向第二次分配的記憶體,我們就丟失對第一次分配的記憶體的控制了,記憶體流失就產生了。程式不斷執行下去,沒有得到釋放的記憶體片段越來越多,直到記憶體耗盡程式崩潰。
因此,動態分配的記憶體在不適用時一定要釋放。比如上面的代碼,可改為:
- int main()
- {
- char *str = NULL;
- for (int i = 0; i < 1000; i++)
- {
- str = new char[100000]; //只有new,沒有delete,將產生記憶體流失
- Sleep(100); //延時100ms,可以開啟工作管理員觀測記憶體變化
- delete []str; //釋放記憶體
- }
- return 0;
- }
好了,到此,小菜對記憶體流失總算是有個直觀的印象了。下面琢磨下cvGetSubRect()函數是怎樣產生記憶體流失的。函數原型:
- CV_IMPL CvMat*
- cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect )
小菜原來的用法:
- ...
- CvMat *firstFrameSub = cvCreateMat( rows, cols, CV_8UC1 ); //建立一個rows*cols 的8-bit 無符號單通道矩陣
- cvGetSubRect(src, firstFrameSub, rect); //此處產生記憶體流失!
- ...
進入cvGetSubRect定義代碼:
- CV_IMPL CvMat*
- cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect )
- {
- CvMat* res = 0;
-
- CV_FUNCNAME( "cvGetRect" );
- __BEGIN__;
- CvMat stub, *mat = (CvMat*)arr;
- if( !CV_IS_MAT( mat ))
- CV_CALL( mat = cvGetMat( mat, &stub ));
- if( !submat )
- CV_ERROR( CV_StsNullPtr, "" );
- if( (rect.x|rect.y|rect.width|rect.height) < 0 )
- CV_ERROR( CV_StsBadSize, "" );
- if( rect.x + rect.width > mat->cols ||
- rect.y + rect.height > mat->rows )
- CV_ERROR( CV_StsBadSize, "" );
- {
- //從此處可以看出,如果submat已經分配有記憶體空間,那麼,此處將產生記憶體流失,切記傳遞submat時,一定要穿一個沒有分配記憶體的指標。
- submat->data.ptr = mat->data.ptr + (size_t)rect.y*mat->step +
- rect.x*CV_ELEM_SIZE(mat->type);
- submat->step = mat->step & (rect.height > 1 ? -1 : 0);
- submat->type = (mat->type & (rect.width < mat->cols ? ~CV_MAT_CONT_FLAG : -1)) |
- (submat->step == 0 ? CV_MAT_CONT_FLAG : 0);
- submat->rows = rect.height;
- submat->cols = rect.width;
- submat->refcount = 0;
- res = submat;
- }
-
- __END__;
- return res;
- }
因此,使用cvGetSubRect時候要非常注意,它的第二個參數不能預先分配記憶體。這和大部分的opencv函數不一樣。
參考資料:
http://blog.csdn.net/Justin4wd/archive/2008/06/26/2588243.aspx