OpenCV提供了一套高效的内存管理方案,提升了内存申请速率,减少了内存碎片,能够很好的提升程序的稳定性,同时支持线程同步。下面是对OpenCV内存管理源码中alloc.cpp的主要函数fastMalloc()和fastFree()的解读研究。
1、启用内存池分配内存
OpenCV3.1中(包括之前的版本),默认是不启用内存池分配的。我的办法是对源码修改后重新编译。步骤如下:
- 在alloc.cpp中添加头文件#include <Windows.h>;
- 将宏CV_USE_SYSTEM_MALLOC改为0;
- 将AutoLock在该cpp中的名字替换为其他名字,如TAutoLock。
重新编译后就可以使用了。
2、原理说明
内存池首先分配了一个
巨大内存块,大小为1MB,然后在其中划分为一个个的
小块,每个小块大小为16KB,每个小块又可按照给定的内存大小分配表划分为29个
类型,将每个小块按类型进行大小
区段均分。申请内存时首先判断该大小的内存属于哪个类型,然后再在某个小块上进行分配工作。
3、基本结构及参数定义说明
- 类型表binSizeTab
static const int binSizeTab[MAX_BIN+1] =
{ 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 128, 160, 192, 256, 320, 384, 480, 544, 672, 768,
896, 1056, 1328, 1600, 2688, 4048, 5408, 8128, 16256 };//29种类型
内存分配查询表MallocTables
struct MallocTables
{
{
uchar binIdx[MAX_BLOCK_SIZE/8+1];//对要分配的内存大小,能够以O(1)的时间复杂度找到对应的块类型大小
}
- 最基本的内存块Block
struct Block{
size_t signature;//标记,表明该内存块是该内存池分配的
Block* prev; //上一个Block,不同情况下的意义不同
Block* next; //下一个Block,不同情况下的意义不同
Node* privateFreeList; //当前块的私有自由区段地址链表
Node* publicFreeList; //
uchar* bumpPtr; //当前小块中第一个可用区段地址,该指针只能往endPtr方向偏移累加
uchar* endPtr; //当前小块中最后一个可用区段的尾地址
uchar* data; //块中偏移头部结构后的数据区
ThreadData* threadData; //线程数据指针
int objSize; //当前块类型的大小,对应binSizeTab中的数值
int binIdx; //当前块类型在内存分配查询表MallocTables中的下标
int allocated; //该区块已分配的区段个数
int almostEmptyThreshold; //阈值判断,与是否将该区块作为可用区块的判断有关
CriticalSection cs; //临界区
}
- 巨大内存块BigBlock
struct BigBlock
{
BigBlock* next;//下一个巨大内存块
Block* first; //巨大内存块中第一个区块地址
int nblocks; //巨大内存块中区块个数
}
- 内存池基本结构BigBlock
struct BlockPool
{
CriticalSection cs;//临界区
Block* freeBlocks; //指向内存池中自由块链表
BigBlock* pool;//指向第一个巨大内存块地址
int bigBlockSize;//巨大内存块的大小
int blocksPerBigBlock;//未使用
}
- 线程数据结构ThreadData
struct ThreadData
{
Block* bins[MAX_BIN+1][3];//用START、FREE控制每个区段的区块双向链表
static DWORD tlsKey;
}
4、内存池结构图