概要

所谓内存池,顾名思义和线程池的设计原理是一样的,为了减少频繁申请释放内存而带来的资源消耗,减少释放内存后产生的内存碎片。

设计理念

为了方便管理内存池的设计通常是划分出一定数量的内存块,这些内存块的长度是一样的; 用户申请内存块时返回空闲的内存块地址,如果内存块使用完毕就释放该内存块,将该内存块置为空闲状态,放回到内存池,供以后使用。

内存池的设计核心几大模块:创建内存池,申请内存块,释放内存块,销毁内存池!

当然这只是常用的内存池设计,实际项目中可以根据需求设计不同的线程池:内存块的长度不一,可以提供自定义的内存块设计等兼容性更高的内存池。

本文只做内存池原理的讲解和实现最基础的内存池!更多的功能根据实际的需求进行扩展即可。

内存池的设计思路有很多,可以给予链表,数组,队列等进行设计,核心就是怎么存储内存块信息;本期是基于链表进行的内存池设计。

模块设计

内存池结构

内存块节点结构

typedef struct MemoryBlock{
void *data;//内存块起始地址
struct MemoryBlock *next;//下一个内存块的地址
}MemoryBlock;

内存池结构

typedef struct MemoryPool{
MemoryBlock *freeList;//空闲内存块链表
MemoryBlock *usedList;//占用内存块链表
int freeCount;//空闲内存块数量
int usedCount;//占用内存块数量
int blockCount;//内存块总数量
}MemoryPool;

创建内存池

通过参数确定内存池中内存块的大小和数量,然后给每个内存块开辟空间,然后初始化空闲链表,占用链表,空闲数量,占用数量等

MemoryPool *InitMemoryPool(int blockSize, int blockCount)
{
MemoryPool *pool = NULL; pool = (MemoryPool *)malloc(sizeof(MemoryPool));//为内存池分配空间
pool->freeList = NULL;
pool->usedList = NULL;
for(int i = 0; i < blockCount; i++)
{
//创建内存块节点,插入到空闲链表
MemoryBlock * block = (MemoryBlock *)malloc(sizeof(MemoryBlock));
block->data = malloc(blockSize);
block->next = pool->freeList;
pool->freeList = block;
}
//初始化状态
pool->freeCount = blockCount;
pool->usedList = 0;
pool->blockCount = blockCount; return pool;
}

申请内存块

将内存池中空闲的内存块提供给用户使用,如果没有空闲内存块返回NULL。

void *AllocateBlock(MemoryPool *pool)
{
if(pool->freeList == NULL || pool->freeCount == 0)
return NULL;
MemoryBlock *node = pool->freeList;
//该内存块从空闲链表删除
pool->freeList = node->next;
//该内存块插入到占用链表
node->next = pool->usedList;
pool->usedList = node;
//更新空闲,占用状态
pool->usedCount++;
pool->freeCount--; return node->data;
}

释放内存块

将内存块放回到内存池

void FreeBlock(MemoryPool *pool, void *data)
{
MemoryBlock *cur = pool->usedList;
MemoryBlock *pre = NULL; //寻找给内存块的节点
while(pre != NULL && cur->data != data)
{
pre = cur;
cur = cur->next;
}
if(cur == NULL)
return;
//将该内存块从占用链表删除
if(pre != NULL)
pre->next = cur->next;
else
pool->usedList = cur->next;
//将该内存块插入到空闲链表
cur->next = pool->freeList;
pool->freeList = cur; pool->freeCount++;
pool->usedCount--; return;
}

销毁内存池

销毁所有的内存块及分配过的空间

void DestroyMemoryPool(MemoryPool *pool)
{
MemoryBlock *pre = NULL;
//释放所有空闲内存块空间
while(pool->freeList != NULL)
{
pre = pool->freeList;
free(pool->freeList->data);
pool->freeList = pool->freeList->next;
free(pre);
}
//释放所有占用内存块空间
while(pool->usedList != NULL)
{
pre = pool->usedList;
free(pool->usedList->data);
pool->usedList = pool->usedList->next;
free(pre);
}
//释放内存池空间
free(pool); pool->freeList = NULL;
pool->usedList = NULL;
pool->freeCount = 0;
pool->usedCount = 0; return;
}

至此一个最基础的内存池算是已经完成,在实际项目中可以在此基础上进行扩展;

main函数调用

int main(void)
{
MemoryPool *pool; pool = InitMemoryPool(10, 5); int *str = (int *)AllocateBlock(pool);
*str = 2;
int *ptr = (int *)AllocateBlock(pool);
*ptr = 3;
printf("free block : %d, used block : %d\n", pool->freeCount, pool->usedCount);
FreeBlock(pool, ptr);
printf("free block : %d, used block : %d\n", pool->freeCount, pool->usedCount); DestroyMemoryPool(pool); return 0;
}

c语言实现内存池的更多相关文章

  1. C语言内存管理(内存池)

    C语言可以使用alloc从栈上动态分配内存. 内存碎片 Malloc/free或者new/delete大量使用会造成内存碎片,这种碎片形成的机理如下: 内存碎片一般是由于空闲的内存空间比要连续申请的空 ...

  2. linux下C语言实现的内存池【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-4254501.html 操作系统:ubuntu10.04 前言:     在通信过程中,无法知道将会接收到的 ...

  3. 高效内存池的设计方案[c语言]

    一.前言概述 本人在转发的博文<内存池的设计和实现>中,详细阐述了系统默认内存分配函数malloc/free的缺点,以及进行内存池设计的原因,在此不再赘述.通过对Nginx内存池以及< ...

  4. 基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...

  5. JVM内存管理------JAVA语言的内存管理概述

    引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓 ...

  6. 【uTenux实验】内存池管理(固定内存池和可变内存池)

    1.固定内存池管理实验 内存管理是操作系统的一个基础功能.uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理.uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看 ...

  7. 对象池与.net—从一个内存池实现说起

    本来想写篇关于System.Collections.Immutable中提供的ImmutableList里一些实现细节来着,结果一时想不起来源码在哪里--为什么会变成这样呢--第一次有了想写分析的源码 ...

  8. loki之内存池SmallObj[原创]

    loki库之内存池SmallObj 介绍 loki库的内存池实现主要在文件smallobj中,顾名思义它的优势主要在小对象的分配与释放上,loki库是基于策略的方法实现的,简单的说就是把某个类通过模板 ...

  9. JVM内存管理之JAVA语言的内存管理概述

    引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓 ...

  10. Boost内存池使用与测试

    目录 Boost内存池使用与测试 什么是内存池 内存池的应用场景 安装 内存池的特征 无内存泄露 申请的内存数组没有被填充 任何数组内存块的位置都和使用operator new[]分配的内存块位置一致 ...

随机推荐

  1. uni-app打包h5页面ios唤起软键盘踩坑

    问题:页面有很多input框,上面的input输入框,当虚拟键盘出来时没问题,但是下面的input输入框,就会出现问题,input输入框会跑到键盘后面. 网上一阵百度,找到原因:安卓手机中唤起软键盘时 ...

  2. Element UI Table合并行

    Vue使用Element-ui Table 合并行,官方只是一个非常简单的合并例子,通常业务都是相同的某个字段进行合并. 效果图 代码实现 1.Table <el-table :data=&qu ...

  3. (保姆级)服务器-Zabbix6.0使用Python脚本实现带图片的邮箱的报警

    前言 近期在琢磨Zabbix邮箱报警的功能,但是网上的教程通常是4.0或5.0版本Zabbix,并使用Python2.7环境,运行在新版本Zabbix6.0上有颇多问题,为此我基于原先教程修改基于Za ...

  4. cancal报错 config dir not found

    替换classpath中间封号两边的值

  5. [转帖]k8s之PV、PVC、StorageClass详解

    https://zhuanlan.zhihu.com/p/128552232 导读 上一篇写了共享存储的概述以及一个简单的案例演示.这一篇就写一下PV和PVC. PV是对底层网络共享存储的抽象,将共享 ...

  6. 【转帖】磁盘IOPS的计算

    计算磁盘IOPS的三个因素: 1.RAID类型的读写比 不同RAID类型的IOPS计算公式: RAID类型 公式 RAID5.RAID3 Drive IOPS=Read IOPS + 4*Write ...

  7. [转帖]shell命令替换~date用法~如果被替换命令的输出内容包括多行或有多个连续的空白符,输出变量时应该将变量用双引号包围

    https://www.cnblogs.com/mianbaoshu/p/12069458.html Shell 命令替换是指将命令的输出结果赋值给某个变量.比如,将使用ls命令查看到的某个目录中的内 ...

  8. [转帖]Systemd 指令

    一.由来 历史上,Linux 的启动一直采用init进程. 下面的命令用来启动服务. $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 s ...

  9. MySQL in Windows安装以及异名恢复的简单过程

    下载相关 建议获取最新版本的Mysql数据库 可以获取 zip 格式的安装文件 https://dev.mysql.com/downloads/mysql/ 或者获取 msi 格式的安装文件 http ...

  10. 批量删除一个月为tag的镜像的办法

    第一步获取镜像列表 这是一个最简单的列转行. docker images |grep 20220401 |awk 'BEGIN{ORS=","}{print $1}' 第二步执行双 ...