多年心血凝聚成的C++高效内存池,告别内存泄露、内存碎片

1. 内存池介绍

内存池是预分配一段连续的内存,然后将它们划分成固定大小块,以便稍后快速分配和释放。内存池主要应用于程序在运行过程中经常申请和释放分配内存的场景。使用内存池可以有效地减少分配和释放内存的时间成本,从而提高程序的执行效率。

内存池有两种方案,一种是大小可变的内存池,一种是大小固定的内存池。大小可变的内存池,各个内存块通过链表连接,空闲的内存块位于链表前段。当程序需要一段内存时,就以此检查可用的内存块,如果大小满足要求,就利用当前这块内存。这里可能会出现一个较小的内存需要缺占据了一个之前动态分配现在已空闲出来的较大内存。

大小固定的内存池,可以开始分配一段大的内存,然后切割成等大小的多块内存。然后用一个额外的链表,记录空闲的内存块。当需要内存时,就从空闲的内存块里查找即可,因为内存块大小固定,因此根据偏移量容易定位内存位置,同时释放整个大内存块也很方便。缺点是这个每段内存块的大小是多少合适很难确定。

本文实现的内存池是结合以上两种情况的优点,初始化时分配多段大内存,然后切分成128,256,512等字节大小的小内存块。而每个大小的内存块如果有多块,又采用一个链表来管理。当需要一个内存时,首先根据内存大小需求到那个值的内存块里去获取,然后遍历该内存块的管理链表,从而获得自己需要的内存。

2. 核心代码

memoryPool.h:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "memoryPool.h"
using namespace SgLib;

int main(int argc, char *argv[])
{
    // false表示线程不安全,不需要同步内存块的锁,效率更高。如果是true,用于线程安全
    MemoryPool g_mp(false);   

    // 初始内存池对象
    // 3:表示初始创建128,256,521三个层级的内存块,2表示每个层级内存块里创建2个内存数据块
    g_mp.init(3, 2);   

    char *pszTmp1 = (char *)g_mp.allocMemory(1000);
    strcpy(pszTmp1, "我爱北京天安门");
    printf("pszTmp1内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp1, pszTmp1);
    g_mp.printInfo();

    char *pszTmp2 = (char *)g_mp.allocMemory(10000);
    strcpy(pszTmp2, "I am a student.我是一个学生");
    printf("pszTmp2内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp2, pszTmp2);
    g_mp.printInfo();
    
    g_mp.freeMemory(pszTmp1);
    g_mp.printInfo();

    char *pszTmp3 = (char *)g_mp.allocMemory(900);
    strcpy(pszTmp3, "ppp");
    printf("pszTmp3内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp3, pszTmp3);
    g_mp.printInfo();

    g_mp.freeMemory(pszTmp1);
    g_mp.freeMemory(pszTmp2);
    g_mp.printInfo();

    return 0;
}
 

在实现文件里,最核心的就是创建内存块函数:

 

void MemoryPool::createOneBlockMemory(size_t siLevel)
{
    size_t siNums = m_siInitNums;
    size_t siBlockSize = pow(2, 6 + (siLevel + 1));
    if (siBlockSize > 1048576)
    {
        siNums = 1;
}
// 每个数据块前是控制块,便于释放时寻找
void *pData = malloc((siBlockSize + sizeof(tFreeDataBlock)) * siNums);
if (pData == NULL)
    {
        return;
    }
    else
    {
        byte *pbtData = (byte *)pData;
        tSrcDataBlock *pSrc = new tSrcDataBlock();
        pSrc->pNext = m_pSrcBlockHead[siLevel];
        pSrc->pDataPos = pbtData;
        m_pSrcBlockHead[siLevel] = pSrc;

        tFreeDataBlock *pPreFree = NULL;
        for (size_t m = 0; m < siNums; ++m)
        {
            tFreeDataBlock *pFree = (tFreeDataBlock *)(pbtData + (int)(m * (siBlockSize + sizeof(tFreeDataBlock))));
            pFree->pDataPos = (byte *)pFree + sizeof(tFreeDataBlock);
            pFree->pNext = NULL;
            pFree->siLevel = siLevel;
            if (m == 0)
            {
                m_pFreeBlockHead[siLevel] = pFree;
            }
            if (pPreFree != NULL)
            {
                pPreFree->pNext = pFree;
            }
            pPreFree = pFree;
        }
    }
}

3. 调用内存池类

如何调用内存池类:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "memoryPool.h"
using namespace SgLib;

MemoryPool g_mp(true);

int main(int argc, char *argv[])
{
    // 初始内存池对象
    g_mp.init(2, 2);

    char *pszTmp1 = (char *)g_mp.allocMemory(1000);
    strcpy(pszTmp1, "我爱北京天安门");
    printf("pszTmp1内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp1, pszTmp1);
    g_mp.printInfo();

    char *pszTmp2 = (char *)g_mp.allocMemory(10000);
    strcpy(pszTmp2, "I am a student.我是一个学生");
    printf("pszTmp2内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp2, pszTmp2);
    g_mp.printInfo();
    
    g_mp.freeMemory(pszTmp1);
    g_mp.printInfo();

    char *pszTmp3 = (char *)g_mp.allocMemory(900);
    strcpy(pszTmp3, "ppp");
    printf("pszTmp3内存地址:0x%lu, 内容:%s\n", (unsigned long)(uintptr_t)pszTmp3, pszTmp3);
    g_mp.printInfo();

    g_mp.freeMemory(pszTmp1);
    g_mp.freeMemory(pszTmp2);
    g_mp.printInfo();

    return 0;
}

4. 工程运行和截图

从截图中,可以看到pszTmp1和pszTmp3的内存地址是一样的,即pszTmp3重用了pszTmp1的内存块(pszTmp1“释放”后)。

完整代码的下载可以参考下面网址:
http://kbase12.com/codedemo/doc/detail?id=80431cdaaf99472c97a1e99547798307

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wave12_mp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值