继续我们的内存分配函数,下面是页的分配
bool MemPool::AllocPage()
{
MemPageHeader *p = (MemPageHeader*) _aligned_malloc(MAX_ALLOC_FROM_PAGE * 2, 16); // 每个页2*2048个字节
if (p == NULL)
return false; // 从系统申请内存失败
p->d.pLast = (char*) p + sizeof(MemPageHeader);
p->d.pEnd = (char*) p + MAX_ALLOC_FROM_PAGE * 2;
p->d.nMax = MAX_ALLOC_FROM_PAGE * 2;
p->postf.pPrefix = NULL;
p->postf.dwCheckSum = PAGE_POSTFIX_SIG; // 设为某个签名,用来表示这个后缀对应于一个页的头部,而不是普通的内存块的后缀。有什么用?想想页的尾巴(pLast)向前移动寻找,看前面的内存块是否可以与尾部的空余内存块合并。怎么知道前面的是个普通内存块还是已经到页的头部了呢?
p->d.pNext = m_pFirstPage;
m_pFirstPage = p;
return true;
}
下面用于申请一块大于2048字节的内存:
void* MemPool::AllocLarge(int nSize, const char* const szFileName, int nLine, bool bIsArray)
{
Prefix *pPrefix = (Prefix*) _aligned_malloc(sizeof(Prefix) + nSize + sizeof(Postfix), 16);
if (pPrefix)
{
pPrefix->d.pPostfix = (Postfix*)(pPrefix->pMem + nSize);
pPrefix->d.pPostfix->pPrefix = pPrefix;
pPrefix->d.pPage = NULL; // 表明它不是从页里分配的
// 插入大内存块的链表头和第一个有效元素(如果有的话)之间
pPrefix->d.pNext = m_pLarge->d.pNext;
if (m_pLarge->d.pNext)
m_pLarge->d.pNext->d.pPrev = pPrefix;
m_pLarge->d.pNext = pPrefix;
pPrefix->d.pPrev = m_pLarge;
return pPrefix->pMem; // 返回真正使用的内存块地址
}
std::cerr << "Memory allocation failure" << std::endl;
return NULL;
}
接下来是我们的另一个重头函数,内存释放函数:
void MemPool::Free(void* pMem)
{
SYNCHRONIZATION(); // 线程安全的宏,这里不用细究
Prefix *pPrefix = (Prefix*) pMem - 1; // 得到对应的前缀地址
if (pPrefix->d.pPage) // 从页里分配的
{
int nSize = (int) ((INT_PTR) pPrefix->d.pPostfix - (INT_PTR) pPrefix->pMem);
int nMinPower2 = 16;
int i = 0;
for (; nMinPower2 < nSize; nMinPower2 = nMinPower2 << 1, i ++); // 得到该尺寸对应的索引
m_nBlocksAllocated[i] --; // 计数分配的该尺寸内存块
MemPageHeader *pPage = pPrefix->d.pPage;
if (pPage->d.pLast == (char*) (pPrefix->d.pPostfix + 1)) // 紧接页尾,可合并
{
pPage->d.pLast = (char*) pPrefix; // 合并
do {
// 继续检查前面的内存块使用中还是在池里(即是否有可以继续合并的)
Postfix *pPostfix = (Postfix*) pPage->d.pLast - 1;
if (pPostfix->pPrefix == NULL && pPostfix->dwCheckSum == PAGE_POSTFIX_SIG)
break; // 已经到页头了
pPrefix = pPostfix->pPrefix;
if (IsInBlockPool(pPrefix)) // 在某个尺寸的内存池中
{
// 从对应尺寸的内存池中分离
Prefix *p1 = pPrefix->d.pPrev, *p2 = pPrefix->d.pNext;
p1->d.pNext = p2;
if (p2)
p2->d.pPrev = p1;
pPage->d.pLast = (char*) pPrefix; // 合并
}
else
break;
} while (true);
}
else // 不在页尾,无法合并
{
// 插入对应尺寸的内存池链表头和第一个有效元素之间
pPrefix->d.pNext = m_ppBlockPools[i]->d.pNext;
if (m_ppBlockPools[i]->d.pNext)
m_ppBlockPools[i]->d.pNext->d.pPrev = pPrefix;
m_ppBlockPools[i]->d.pNext = pPrefix;
pPrefix->d.pPrev = m_ppBlockPools[i];
pPrefix->d.pPostfix->nBlockPoolIndex = i;
}
}
else // 大内存
{
// 将该内存块从大内存链表中分离
Prefix *p1 = pPrefix->d.pPrev, *p2 = pPrefix->d.pNext;
p1->d.pNext = p2;
if (p2)
p2->d.pPrev = p1;
// 释放
_aligned_free(pPrefix);
}
}
好了,内存池的实现就这些了,除了还有一个添加清理函数的东西还没放进来。这个先不弄了。以后再说。
什么,说好的overload的new和delete呢?现在这两个Alloc和Free函数怎么用?
下回再说