深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
上述只是一个简单的逻辑讲解,比较宏观,下面我们将通过图解和代码的方式来进行讲解。
设计
在上图中,我们画出了内存池的结构图,从图中,可以看出,有两个结构变量,分别为MemoryPool和MemoryBlock。
下面我们将从数据结构和接口两个部分出发,详细讲解内存池的设计。
数据结构
MemoryBlock
本文中所讲述的内存块的分配和释放都是通过该结构进行操作,下面是MemoryBlock的示例图:
在上图中,Header存储该MemoryBlock的内存块情况,比如可用的内存块索引、当前MemoryBlock中可用内存块的个数等等。
定义如下所示:
struct MemoryBlock {
unsigned int size;
unsigned int free_size;
unsigned int first_free;
struct MemoryBlock \*next;
char a_data[0];
};
其中:
- size为MemoryBlock下内存块的个数
- free_size为MemoryBlock下空闲内存块的个数
- first_free为MemoryBlock中第一个空闲块的索引
- next指向下一个MemoryBlock
- a_data是一个柔性数组
柔性数组即数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。
它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题。
MemoryPool
MemoryPool为内存池的头,里面定义了该内存池的信息,比如本内存池分配的固定对象的大小,第一个MemoryBlock等
struct MemoryPool {
unsigned int obj_size;
unsigned int init_size;
unsigned int grow_size;
MemoryBlock *first_block;
};
其中:
- obj_size为内存池分配的固定内存块的大小
- init_size初始化内存池时候创建的内存块的个数
- grow_size当初始化内存块使用完后,再次申请内存块时候的个数
- first_block指向第一个MemoryBlock
接口
memory_pool_create
MemoryPool \*memory\_pool\_create(unsigned int init_size,
unsigned int grow_size,
unsigned int size);
本函数用来创建一个MemoryPool,并对其进行初始化,下面是参数说明:
- init_size 表示第一个MemoryBlock中创建块的个数
- grow_size 表示当MemoryPool中没有空闲块可用,则创建一个新的MemoryBlock时其块的个数
- size 为块的大小(即每次分配相同大小的固定size)
memory_alloc
void \*memory\_alloc(MemoryPool \*mp);
本函数用了从mp中申请一块内存返回
- mp 为MemoryPool类型指针,即内存池的头
- 如果内存分配失败,则返回NULL
memory_free
void\* memory\_free(MemoryPool \*mp, void \*pfree);
本函数用来释放内存
- mp 为MemoryPool类型指针,即内存池的头
- pfree 为要释放的内存
free_memory_pool
void free\_memory\_pool(MemoryPool \*mp);
本函数用来释放内存池
实现
在讲解整个实现之前,我们先看先内存池的详细结构图。
初始化内存池
MemoryPool是整个内存池的入口结构,该函数主要是用来创建MemoryPool对象,并使用参数对其内部的成员变量进行初始化。
函数定义如下:
MemoryPool \*memory\_pool\_create(unsigned int init_size, unsigned int grow_size, unsigned int size)
{
MemoryPool \*mp;
mp = (MemoryPool\*)malloc(sizeof(MemoryPool));
mp->first_block = NULL;
mp->init_size = init_size;
mp->grow_size = grow_size;
if(size < sizeof(unsigned int))
mp->obj_size = sizeof(unsigned int);
mp->obj_size = (size + (MEMPOOL_ALIGNMENT-1)) & ~(MEMPOOL_ALIGNMENT-1);
return mp;
}
内存分配
void \*memory\_alloc(MemoryPool \*mp) {
unsigned int i;
unsigned int length;
if(mp->first_block == NULL) {
MemoryBlock \*mb;
length = (mp->init_size)\*(mp->obj_size) + sizeof(MemoryBlock);
mb = malloc(length);
if(mb == NULL) {
perror("memory allocate failed!\n");
return NULL;
}
/\* init the first block \*/
mb->next = NULL;
mb->free_size = mp->init_size - 1;
mb->first_free = 1;
mb->size = mp->init_size\*mp->obj_size;
mp->first_block = mb;
char \*data = mb->a_data;
/\* set the mark \*/
for(i=1; i<mp->init_size; ++i) {
\*(unsigned long \*)data = i;
data += mp->obj_size;
}
return (void \*)mb->a_data;
}
MemoryBlock \*pm_block = mp->first_block;
while((pm_block != NULL) && (pm_block->free_size == 0)) {
pm_block = pm_block->next;
}
if(pm_block != NULL) {
char \*pfree = pm_block->a_data + pm_block->first_free \* mp->obj_size;
pm_block->first_free = \*((unsigned long \*)pfree);
pm_block->free_size--;
return (void \*)pfree;
} else {
if(mp->grow_size == 0)
return NULL;
MemoryBlock \*new_block = (MemoryBlock \*)malloc((mp->grow_size)\*(mp->obj_size) + sizeof(MemoryBlock));
if(new_block == NULL)
return NULL;
char \*data = new_block->a_data;
for(i=1; i<mp->grow_size; ++i) {
\*(unsigned long \*)data = i;
data += mp->obj_size;
}
new_block->size = mp->grow_size\*mp->obj_size;
new_block->free_size = mp->grow_size-1;
new_block->first_free = 1;
new_block->next = mp->first_block;
mp->first_block = new_block;
return (void \*)new_block->a_data;
}
}
内存块主要在MemoryBlock结构中,也就是说申请的内存,都是从MemoryBlock中进行获取,流程如下:
- 获取MemoryPool中的first_block指针
- 如果该指针为空,则创建一个MemoryBlock,first_block指向新建的MemoryBlock,并返回
- 否则,从first_block进行单链表遍历,查找第一个free_size不为0的MemoryBlock,如果找到,则对该MemoryBlock的相关参数进行设置,然后返回内存块
- 否则,创建一个新的MemoryBlock,进行初始化分配之后,将其插入到链表的头部(这样做的目的是为了方便下次分配效率,即减小了链表的遍历)
在上述代码中,需要注意的是第30-33行或者67-70行,这两行的功能一样,都是对新申请的内存块进行初始化,这几行的意思,是要将空闲块连接起来,但是,并没有使用传统意义上的链表方式,而是通过index方式进行连接,具体如下图所示:
在上图中,第0块空闲块的下一个空闲块索引为1,而第1块空闲块的索引为2,依次类推,形成了如下链表方式
1->2->3->4->5
内存分配流程图如下所示:
内存释放
void\* memory\_free(MemoryPool \*mp, void \*pfree) {
if(mp->first_block == NULL) {
return;
}
MemoryBlock \*pm_block = mp->first_block;
MemoryBlock \*pm_pre_block = mp->first_block;
/\* research the MemoryBlock which the pfree in \*/
while(pm_block && ((unsigned long)pfree < (unsigned long)pm_block->a_data ||
(unsigned long)pfree>((unsigned long)pm_block->a_data+pm_block->size))) {
//pm\_pre\_block = pm\_block;
pm_block = pm_block->next;
if(pm_block == NULL) {
return pfree;
}
}
unsigned int offset = pfree -(void\*) pm_block->a_data;
if((offset&(mp->obj_size -1)) > 0) {
return pfree;
}
pm_block->free_size++;
\*((unsigned int \*)pfree) = pm_block->first_free;
pm_block->first_free=(unsigned int)(offset/mp->obj_size);
return NULL;
}
内存释放过程如下:
-
判断当前MemoryPool的first_block指针是否为空,如果为空,则返回
-
否则,遍历MemoryBlock链表,根据所释放的指针参数判断是否在某一个MemoryBlock中
- 如果找到,则对MemoryBlock中的各个参数进行操作,然后返回
- 否则,没有合适的MemoryBlock,则表明该被释放的指针不在内存池中,返回
在上述代码中,需要注意第20-29行。
- 第20行,求出被释放的内存块在MemoryBlock中的偏移
- 第22行,判断是否能被整除,即是否在这个内存块中,算是个double check
- 第26行,将该MemoryBlock中的空闲块个数加1
- 第27-29行,类似于链表的插入,将新释放的内存块的索引放入链表头,而其内部的指向下一个可用内存块
现在举个例子,以便于理解,假设在一开始有5个空闲块,其中前三个空闲块都分配出去了,那么此时,空闲块链表如下
4->5,其中first_free = 4
然后在某一个时刻,第1块释放了,那么释放归还之后,如下
1->4->5,其中first_free = 1
内存释放流程图如下:
释放内存池
void free\_memory\_pool(MemoryPool \*mp) {
MemoryBlock \*mb = mp->first_block;
if(mb != NULL) {
while(mb->next != NULL) {
s_memory_block \*delete_block = mb;


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
first_block;
if(mb != NULL) {
while(mb->next != NULL) {
s_memory_block \*delete_block = mb;
[外链图片转存中...(img-HzCRMTuG-1715732222312)]
[外链图片转存中...(img-whXsMMtR-1715732222312)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**