一、内存池概述
- 服务器会频繁地对内存进行申请和释放,频繁地操作将带来如下的缺点:
- 服务器性能降低,因为需要频繁地申请和释放内存
- 内存碎片化多
- 等等......
- 市场上开源的比较好用的内存池有:
- tcmalloc:MySQL使用的
- jemalloc:Tomcat使用的
- Nginx也有一套自己的内存池(本文就是仿照Nginx的内存池设计的)
二、内存池设计
- 因为内存页的大小为4K,因此在设计内存池的时候,我们将认为:
- <4k的内存:称为小内存块
- >4K的内存:称为大内存快
struct mp_large_s(大内存块结构)
- 当我们申请的内存大于4K时,就会申请一个大的内存块,这个结构体就是大内存结构
- 该结构不是真正存储内存的,真正的内存是由其alloc成员所指向的
- 如上结构图所示(蓝色部分):
- alloc:指向真正的大内存块地址
- next:指向下一个struct mp_large_s结构的指针
//大内存块节点 struct mp_large_s { void *alloc; //指向该大内存快的实际存储区域 struct mp_large_s *next; //指向下一块大内存快 };
struct mp_node_s(大内存块结构)
- 当我们申请的内存小于4K时,就会申请一个小的内存块,这个结构体就是小内存结构
- 与struct mp_large_s结构一样,该结构不是真正存储内存的,真正的内存是在结构后面的data区域存储的
- 如上结构图所示(蓝色部分):
- last:指向后面data区域中可用内存的起始地址
- end:指向后面data区域最后的指针
- next:指向下一个struct mp_node_s结构的指针
- failed:表示当前内存块是否停止使用
//小内存块节点 struct mp_node_s { unsigned char *last; //后面数据存储区域中可用区域的起点 unsigned char *end; //后面数据存储区域的终点 struct mp_node_s *next; //指向下一块小内存块 int failed; //该内存块是否不再使用 };
struct mp_pool_s(内存池)
- 该结构是内存池的真正结构,我们操作内存池时就是操作这个结构
- 如上结构图所示(蓝色部分):
- max:区别大内存块与小内存块的界值,上面我们提到过了,我们本案例以4K为例
- current:指向小内存块链表的起点
- large:指向大内存块链表的起点
- head[0]:柔性数组,struct mp_node_s head[0]相当于struct mp_node_s *head。用来指向于current链表中的第一个struct mp_node_s节点,通过这个成员可以方便的操作current链表
//内存池,管理者所有的内存块 struct mp_pool_s { //区别大内存块与小内存块的界值: <max的用struct mp_node_s表示, >max的用struct mp_large_s表示 int max; struct mp_node_s *current; //管理小内存块的链表起点 struct mp_large_s *large; //管理大内存快的链表起点 //柔型数组: head[0]指向current链表第1个节点, head[1]指向current链表第2个节点...以此类推 //用这个数组来操作每个mp_node_s节点相关指标(比如更改last等),不需要再去遍历 struct mp_node_s head[0]; };
三、内存池接口设计
- 下面是内存池的相关接口
四、代码实现
五、运行效果
- 该代码正在修正,最近几天完成
来源:oschina
链接:https://my.oschina.net/u/4300166/blog/4463161