一、线程池的引入
-
服务器在高并发下,有大量的客户端连接时,服务器要解析客户端发来的数据,就需要频繁对内存进行申请和释放,使得服务器性能的降低和内存的碎片化增多。
-
开源的比较好用的内存池有:
- 1,tcmalloc:MySQL使用的 ;
- 2,jemalloc:Tomcat使用的 ;
- 3,Nginx实现的内存池。
这里是仿照nginx实现的内存池;
二、内存池的设计
-
我们该如何实现内存池?
内存池是一个组件,做出来以后,提供API给别人使用,在此提一个组件实现的通用方法:- 1,宏定义来处理
- 2,结构体定义出来,基础数据结构定义出来
- 3,对应的函数,对外提供API
- 4,测试函数
-
1,首先我们实现基础数据的结构:
- 小于等于4k的内存,我们称为小块内存;
- 大于的内存,我们称为小块内存;
- 管理大块内存与小块内存的组件数据结构-----内存池组件;
struct mp_large_s(大内存块结构)
* 当我们申请的内存大于4K时,就会申请一个大的内存块,这个结构体就是大内存结构;
* 该结构不是真正存储内存的,真正的内存是由其alloc成员所指向的内存
//大内存节点
struct mp_large_s{
void *alloc; //指向大块的内存
struct mp_large_s *next; //指向下一个struct mp_large_s结构的指针
};
struct mp_node_s(大内存块结构)
* 当我们申请的内存小于4K时,就会申请一个小的内存块,这个结构体就是小内存结构;
* 与struct mp_large_s结构一样,该结构不是真正存储内存的,真正的内存是在结构后面的data区域存储的;
//小内存块节点
struct mp_node_s{
unsigned char *start; // 后面数据存储区域中可用区域的起点
unsigned char *end; // 后面数据存储区域的终点
struct mp_node_s *next; // 内存不够的malloc一块,使用链表连接
int failed; // 用来表示遍历使用的次数
};
struct mp_pool_s(内存池组件)
* 该结构是内存池的真正结构,我们操作内存池时就是操作这个结构;这里使用了柔性数组head[0],可以自行百度下。
//内存池,管理者所有的内存块
struct mp_pool_s{
size_t max; // 区别大内存块与小内存块的界值: <max的用struct mp_node_s表示, >max的用struct mp_large_s表示
struct mp_node_s *current; // 管理小内存块的链表起点
struct mp_large_s *large; //管理大内存快的链表起点
//柔性数组,加一个标签
struct mp_node_s head[0]; //柔型数组: head[0]指向current链表第1个节点, head[1]指向current链表第2个节点...以此类推
//用这个数组来操作每个mp_node_s节点相关指标(比如更改last等),不需要再去遍历
};
- 2,内存池数据结构图如下(盗用了别人的图):
三、内存池API设计
内存池API函数(注意:mp_free()函数只释放大内存。为了提高效率,小块内存是不释放的,直到内存池销毁掉,才释放):
//内存池初始化函数
struct mp_pool_s * mp_create_pool(size_t size);
//内存池销毁函数;mp_large_s指针未释放?并不是,因为mp_large_s内存从pool中分配,小块内存一起释放掉;
void mp_destroy_pool(struct mp_pool_s *pool);
//使用内存池申请内存(大块或者小块)
void *mp_alloc(struct mp_pool_s *pool, size_t size);
//使用内存池申请内存,并将申请的内存置0,使用mp_alloc()实现内存申请,使用memset封装了一层
void *mp_calloc(struct mp_pool_s * pool, size_t size);
//释放大内存,为了提高效率,小块内存是不释放的,直到内存池被销毁掉,才释放;
void mp_free(struct mp_pool_s *pool, void *p);
//内存池重置初始化状态
void mp_reset_pool(struct mp_pool_s * pool);
//内部函数
//小块内存申请函数,提供给mp_alloc()使用
static void *mp_alloc_block(struct mp_pool_s *pool, size_t size);
//大块内存申请函数,提供给mp_alloc()使用
static void *mp_alloc_large(struct mp_pool_s *pool, size_t size);
源码见: https://github.com/Fang-create/memory_pool.git
四、测试函数
使用内存池,申请内存和释放内存测试,见main函数,运行效果如下图。
在此,实现了一个内存池。如果要看内存池的具体使用,可以看看nginx的源码。