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

本文详细介绍了内存池的实现,包括页的分配、大块内存的申请与释放,涉及内存管理的关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

下回再说

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值