一个C++的内存池和内存管理的实现(三)

来源:互联网 发布:111111的网络语意思 编辑:程序博客网 时间:2024/04/28 01:06

继续我们的内存分配函数,下面是页的分配

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函数怎么用?

下回再说

0 0