PainterEngine 内存池实现与细节

本文介绍了PainterEngine内存池的实现,包括内存池的初始化、内存的分配与回收,以及内存碎片管理。通过轻量级的设计,适用于嵌入式系统和小规模内存管理。内存池通过记录分配节点,避免使用链表,减少内存浪费。在内存释放时,通过合并相邻内存节点来减少碎片。此外,文章还讨论了内存分配和释放策略,以减少碎片并提高内存利用率。
摘要由CSDN通过智能技术生成

List item

内存池作为一种的内存管理机制被广泛地运用于各种领域当中,内存池拥有快速的内存分配与更加健壮的管理机制,同时在不同的平台与环境当中也拥有不同的实现方式,本文提出一种轻量级的内存池实现,可以非常方便的移植到内存空间较小的平台当中,可运用在不同的嵌入式平台,服务端及小范围的内存管理当中.

从内存开始

巧妇难为无米之炊,既然我们要说是内存池,显然你首先至少需要找到一个能存储数据的东西,否者接下来都没有讨论的意义,存储的意义可以非常的广泛,它可以是名正言顺的RAM,也可以是你的硬盘,甚至是你的显存或者SD卡当中,为了给PainterEngine的内存池指定内存地址,你需要预先给出一块可用内存的大小与及它的起始地址.例如下面的代码将会给你做一个示范
//为内存池分配1M的内存空间,MemoryPool指向首地址
void *MemoryPool=malloc(1024)
//MemoryPool指向地址0x08600000地址,这块地址可能指向一个片外RAM,一块NANDFlash地址或者其它的任意存储设备
void MemoryPool=(unsigned char)0x08600000
;//MemoryPool可存在于堆中也可存在于栈当中,但是你需要确保它的地址在内存池的使用阶段都是有效的。
char MemoryPool[1024];

可以看到,PainterEngine的内存池可以存在于任何的地址与区域当中,你需要确保的只有三点,1.内存地址是线性的,2.内存是可读的 3.内存是可写的.

一块内存

取得一块内存区域并不是什么非常困难的事情,在windows或者是linux当中,你可以使用现有的malloc或者C++的new来申请一块可用的内存地址,在嵌入式设备当中,你也可以使用片上RAM或者是类似于FSMC或外部地址总线等方式来指定一块内存,具体如何实现由您决定。

那么现在我们假设你已经拥有了这一块可用的内存并且做好了其它的一些别的准备工作,为了方便,我们假设您的内存地址是从0x00000000开始的,并且我们假设它的大小是Size,为了说明方便我们使用图1来表示。

在这里插入图片描述

图1
从上面来看我们已经初始化一些基本的数据并且可以推导一些有用的数据,首先我们有一块内存,而且它的大小是Size,当中的所有区域我们都是可用的。我们还知道它的结束地址,是0x00000000+size-1;

那么既然是内存池,最基本的一个功能当然是可以从内存池当中分割出一块内存来供我们使用,这有点儿像分饼干,假如你读过一些内存池相关的文章,也许现在我们普遍的做法就是把这一块内存区域均等的分为多个block,比如假如这里的size是512字节,假如每个block占64字节,那么这个内存区域就会被分为8个block,当然还有更进一步,4个block组成一个chunk,那么512字节的内存区域,就包含有2个chunk或者说包含有8个block,当然,这里仅仅只是用512字节作为一个比方,也许你觉得这样分是在是多此一举,但假如这个size大到64M或者是128M,那么,用一个chunk为单位,对内存池进行寻址,显然会比你用字节为单位进行寻址要快得多,并且寻址范围也会大的多,并且以chunk(或者别的更大的单位)进行内存管理,对内存碎片的整理合并也会方便且快得多而且很容易使用map进行映射,这样就能够非常快的寻址处理,这种思想被广泛的运用在了文件系统与内存管理方面,例如windows操作系统就会建立一个页目录及页表来管理其内存空间,采用这种映射方式,它允许其内存在物理上不是连续的,甚至映射到硬盘或其他存储设备当中(虚拟内存)。

分割一块可用内存

但遗憾的是,PainterEngine并不将内存进行这种分割处理,我们可以假如我们将一个Block分割为64字节,假设我需要一个86字节的内存空间,那么我们需要2个block也就是128字节,因为block作为一个整体是不可分的,那么我们将有42字节被白白的浪费了,在内存以G为单位的PC机上,也许这些损失实在是无足轻重,然而对于仅仅只有几百kb甚至是几kb的片上系统来说,这些损失就显得实在是太过于昂贵了。你也许会考虑到进一步缩小block提高细分度来减少这种损失,但是,即便是对block进行的寻址,也是需要内存空间的,并且更小的细粒度除了浪费内存之外,也显得毫无意义

技术不是扯淡,更不是拿起一本书就开始照本宣科,脱离实际地区域争论某种高深架构或者方法如何优秀,只是为了掩饰自己能力的贫庸.我们再来看看PainterEngine面向的环境.

1.轻量级内存池,首先决定了它不能过于的臃肿与庞大,在保证功能的前提之下,能多简单就多简单,没必要把简单的问题复杂化

2.内存分配较小。这就意味着使用映射表的方法意义实在不大,也许1000010000的复杂度会很大的优于100lg100,但是10lg10和1010的差别确实不怎么大,即使我们建立的内存表都用链表来进行管理,因为内存较小,表也不大,即使是遍历也未必会比映射慢上多少

3.假如我们要记录一块分配内存,至少需要哪些信息呢,一.这块内存的起始地址

二.这块内存的结束地址或者它的大小。

那么,问题就开始变得简单了,假如内存池是一条等待切割的绳子,现在一个人想要3米长的绳子,那么非常简单,0-3米我们规定就是他的了,又一个人想要2米的绳子,那么我们再规定3m-5m这一段距离的绳子就属于它的了。为了记录下一次我们将从哪里开始切割绳子并且我们也需要知道剩下绳子的长度,以免我们只剩下1m的绳子了仍然答应一个想要2m长绳子的家伙,因此我们需要2个变量来进行记录1.下一次分割绳子的开始点,2.剩余绳子的长度。也就是下一次分配内存的起始地址和剩余内存的大小。那么,最基本的内存分配就这样实现了

我们先定义内存池的基本结构
typedef struct _MemoryPool
{
void *AllocAddr;//下一次内存的分配地址
void *EndAddr;//内存池的尾地址
unsigned int Size;//内存池的总大小
unsigned int FreeSize;//剩余内存池空间大小}MemoryPool;

              我们定义一个函数,用来初始化这个内存池void CreateMemoryPool(void *MemoryAddr,unsigned int MemorySize);
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值