opencv内存管理

来源:互联网 发布:淘宝设置图盾 编辑:程序博客网 时间:2024/05/24 23:13

cvCreateMemStorage调用过程:
CvMemStorage* cvCreateMemStorage(int block_size) —>void* cvAlloc( size_t size )—>
static void* icvDefaultAlloc( size_t size, void* )—->void* cvAlignPtr( const void* ptr, int align=32 )

 

/* creates root memory storage *
CV_IMPL CvMemStorage*
cvCreateMemStorage( int block_size )
{
CvMemStorage *storage = 0;

CV_FUNCNAME( “cvCreateMemStorage” );

__BEGIN__;

CV_CALL( storage = (CvMemStorage *)cvAlloc( sizeof( CvMemStorage )));
CV_CALL( icvInitMemStorage( storage, block_size ));

__END__;

if( cvGetErrStatus() < 0 )
cvFree( &storage );

return storage;
}

先说说比较简单的函数icvInitMemStorage( storage, block_size ):
icvInitMemStorage( CvMemStorage* storage, int block_size )
{////
首先将待分配的空间大小调节成8字节的倍数,然后将传入的CvMemStorage结构体中的字段都置为零,接着初始化其内存块区域的大小。
CV_FUNCNAME( “icvInitMemStorage ” );

__BEGIN__;
if( !storage )
CV_ERROR( CV_StsNullPtr, “” );

if( block_size <= 0 )
block_size = CV_STORAGE_BLOCK_SIZE;

block_size = cvAlign( block_size, CV_STRUCT_ALIGN );
//
(CV_STRUCT_ALIGN=sizeof(double))==8
//待分配的空间大小调节成8字节的倍数
assert( sizeof(CvMemBlock) % CV_STRUCT_ALIGN == 0 );

memset( storage, 0, sizeof( *storage ));
storage->signature = CV_STORAGE_MAGIC_VAL;
storage->block_size = block_size;
__END__;
}

接下来说说与分配相关的函数,

CV_IMPL void* cvAlloc( size_t size )
{
void* ptr = 0;

CV_FUNCNAME( “cvAlloc” );

__BEGIN__;

if( (size_t)size > CV_MAX_ALLOC_SIZE )
CV_ERROR( CV_StsOutOfRange,
“Negative or too large argument of cvAlloc function” );

ptr = p_cvAlloc( size, p_cvAllocUserData );
if( !ptr )
CV_ERROR( CV_StsNoMem, “Out of memory” );

__END__;

return ptr;
}

其中p_cvAlloc就是函数icvDefaultAlloc 函数的函数指针:

static CvAllocFunc p_cvAlloc = icvDefaultAlloc;
p_cvAllocUserData 是全局指针,初始化为NULL

static void*
icvDefaultAlloc( size_t size, void* )
{
char *ptr, *ptr0 = (char*)malloc(
(size_t)(size + CV_MALLOC_ALIGN*((size >= 4096) +1)+ sizeof(char*)));

if( !ptr0 )
return 0;
// align the pointer
ptr = (char*)cvAlignPtr(ptr0 + sizeof(char*) + 1, CV_MALLOC_ALIGN);
//#define CV_MALLOC_ALIGN    32
*(char**)(ptr – sizeof(char*)) = ptr0;
return ptr;
}

使用的是C语言中的malloc函数, 在分配的时候多申请了CV_MALLOC_ALIGN*((size >= 4096) + 1) + sizeof(char*)大小的空间. 多申请空间的用处暂时先不分析.
下面的cvAlignPtr函数用于将指针对其到CV_MALLOC_ALIGN边界, 对于我们常规的PC来说是32bit, 也就是4字节. cvAlignPtr函数将指针调整到32的整数倍。
下面语句将ptr0记录到(ptr – sizeof(char*)), 可以把它看作一个指针. 最后返回ptr.
细心的朋友可能会发现, 前面malloc分配的是ptr0, 现在返回的却是ptr, 这个是为什么呢?

这个的原因还是先放下(我也不懂), 但是返回ptr而不返回ptr0带来的影响至少有2个:

1. 返回的ptr指针不能通过C语言的free函数释放(这也是cvAlloc/cvFree必须配对使用的原因).
2. 在cvFree的时候, 可以根据(ptr – sizeof(char*))对应的值来检测该内存是不是由icvDefaultAlloc申请.
这样应该说可以增加程序的健壮性, icvDefaultFree可以不傻瓜似的对于任何指针都进行释放.

问题1:在malloc分配时表达式中为什么要加这个size >= 4096?
问题2:内存对齐指针调整函数cvAlignPtr,里ptr0 + sizeof(char*) 就行了,怎么还要加一个1啊?

以下对函数icvDefaultAlloc的解析来自opencv论坛http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=5062

作者的内存管理思路是,align之后的ptr之前的4个byte(这里姑且认为是32位平台,指针是32 bits,举例而已,不必较真),用来记录原始的ptr0,也就是从libc的malloc中分配回来的原始内存地址,因为稍后还需要释放。也就是说,从单个slice来看,经过icvDefaultAlloc之后的内存布局为(下面示意图中,4是magic number,这里假设一个指针就是4个byte,size是用户实际需要分配的内存大小):
ptr0———– ptr-4—ptr—–ptr+size
___________________________________
|maybe blank| ptr0 | slice |maybe blank|
————————————————-
例如:ptr0=0×00ae6db8 ;size=56;则有 ptr=0×00ae6dc0,ptr-4=0×00ae6dbc
*(ptr-4)存放的是0×00ae6db8;

6db8—- ——–6dbc——-6dc0—–ptr+size
___________________________________
|maybe blank|   6db8     | slice |maybe blank|
————————————————-
有了这个图,再来看你的两个问题:
请问:在malloc分配时表达式中为什么要加这个size >= 4096?
内存对齐指针调整函数cvAlignPtr,里ptr0 + sizeof(char*) 就行了,怎么还要加一个1啊?

先回答第二个问题,为什么要加1,这就是作者独具匠心的地方所在了,加个1之后,就能保证任何情况下,经过align之后的ptr-4都不等于ptr0,也就是上图最前面的maybe blank区域是始终存在的。这样有什么作用,因为ptr-4里存的东西太重要了(废话,没了这个将来怎么释放这块内存),所以为了防止程序员写出内存越界的代码意外的将ptr-4中的内容覆盖掉了,才在他的前面留些空间出来,这样能够容忍一定程度的内存越界。

再来看第一个问题,为什么要加一个size >= 4096,这里我估计也是作者特意的安排,因为作者认为当用户程序分配大内存块时,更加容易出现内存越界的现象,比如memcpy的长度过大,或strcpy的源字符串太长,所以作者用了这个magic,是的上面示意图中最后面的maybe blank区域扩大了,这样就能容忍更大程度的内存越界。为什么呢,因为某些系统上,可能两次malloc的内存是相邻的,那么前一块越界就可能破坏后一块的数据,尤其是后一块的那个ptr-4。

原创粉丝点击