• 内存池的实现与场景分析


    目录

     

    1.为什么要有内存池?

    2.内存池的实现

    方案一:链表

    方法二:固定块大小

    方案三

    block用于管理小块内存

    整体框架

     实现代码


    1.为什么要有内存池?

    内存池是对堆上申请出来的空间进行管理的。

    因为频繁分配内存,并且每次只占用一点内存,会出现很多的内存碎片。内存池就是为了避免频繁分配,释放以及减少内存碎片的问题。

    2.内存池的实现

    1. 分配的大小是不确定的。
    2. 什么时候分配不确定。

    方案一:链表

    链表节点,节点结构体有四个内容,第一个是内存块地址的指针,第二个指向下一个链表节点,第三个是每个节点还有(flag标志)分配的内存大小,第四个是否可以使用。

    使用方式:

    如果有新的分配请求,就会依次遍历,查找节点(flag来标记是否已经分配或者释放),用该节点分配的内存。如果没有,就会在末尾添加新的节点。

    存在缺点:

    如果第一次使用64字节的内存块,并释放了,下次再使用50字节的呢,需要在末尾继续添加,这样会造成很多无法利用的内存碎片的产生。

    方法二:固定块大小

    分配固定内存块大小,划分大小的等级,符合要求最小大小等级的内存块,分配给它,这样可以避免生成太多碎片。通过索引找到对应的固定内存链表,遍历链表,找到能用的结点。

    缺点:
    查找速度慢(使用链表),小块的回收会出现间隙

    方案三

    对于内存可以 划分为大块(4k)和小块(大于4K)

    大块

            可以划定一个值,对大于该值的可以认定为大块,小于该值可以认定为小块,大块直接用链表去存储(因为内存池最关心的,以及解决的是小块的内存碎片问题,而大块是最希望看到的,不需要内存池去解决,它的碎片少,因此使用链表去管理就行)

    小块

            对于小块,可以放入一个比较大的块(block)里面,在block里面分配一块空间给小块去使用。如果当前block内小块都没在使用了,那么就将整个block一起释放。block之间也用链表进行连接起来,可以动态地管理分配和释放block(这些都是用来存储小块内存)。通过block可以方便小块内存地管理,可以减少内存碎片。

    block用于管理小块内存

    一个block的头部,有last,end,next以及其他参数组成,通过链表将blocks组织起来(next指针)

     例如block大小为4k,需要使用一块8bytes的内存空间,只要把8bytes的首地址(last指向的内存)返回即可,然后将last指向8bytes的内存空间末尾,用于下一次分配使用

    整体框架

    小块往右边的block里面放。大块往下边直接整块从链表头部插入进去。

     实现代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define MP_ALIGNMENT 32
    7. #define MP_PAGE_SIZE 4096
    8. #define MP_MAX_ALLOC_FROM_POOL (MP_PAGE_SIZE-1)
    9. #define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
    10. //用来做对齐的,比如以4个字节对齐, 那么0x35-->0x36
    11. #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
    12. //大块
    13. struct mp_large_s {
    14. struct mp_large_s *next;
    15. void *alloc;
    16. };
    17. //block的头
    18. struct mp_node_s {
    19. unsigned char *last; //指向下一个空余的内存开始地址
    20. unsigned char *end; //指向当前block末尾
    21. struct mp_node_s *next; //指向下一个block
    22. size_t failed;
    23. };
    24. struct mp_pool_s {
    25. size_t max;//能分配的最大size
    26. struct mp_node_s *current; //block当前的指针(遍历查找的起点)
    27. struct mp_large_s *large; //大块
    28. struct mp_node_s head[0]; //第一个block(第一个block是放在mp_pool_s中的,剩下的用链表连接。大小为 头+数据,因为数据大小size不确定,因此用柔性数组)
    29. };
    30. struct mp_pool_s *mp_create_pool(size_t size);
    31. void mp_destory_pool(struct mp_pool_s *pool);
    32. void *mp_alloc(struct mp_pool_s *pool, size_t size);
    33. void *mp_nalloc(struct mp_pool_s *pool, size_t size);
    34. void *mp_calloc(struct mp_pool_s *pool, size_t size);
    35. void mp_free(struct mp_pool_s *pool, void *p);
    36. //API
    37. struct mp_pool_s *mp_create_pool(size_t size) {
    38. struct mp_pool_s *p;
    39. int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s));
    40. if (ret) {
    41. return NULL;
    42. }
    43. p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
    44. p->current = p->head;
    45. p->large = NULL;
    46. p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
    47. p->head->end = p->head->last + size;
    48. p->head->failed = 0;
    49. return p;
    50. }
    51. //API
    52. void mp_destory_pool(struct mp_pool_s *pool) {
    53. struct mp_node_s *h, *n;
    54. struct mp_large_s *l;
    55. for (l = pool->large; l; l = l->next) {
    56. if (l->alloc) {
    57. free(l->alloc);
    58. }
    59. }
    60. h = pool->head->next;
    61. while (h) {
    62. n = h->next;
    63. free(h);
    64. h = n;
    65. }
    66. free(pool);
    67. }
    68. void mp_reset_pool(struct mp_pool_s *pool) {
    69. struct mp_node_s *h;
    70. struct mp_large_s *l;
    71. for (l = pool->large; l; l = l->next) {
    72. if (l->alloc) {
    73. free(l->alloc);
    74. }
    75. }
    76. pool->large = NULL;
    77. for (h = pool->head; h; h = h->next) {
    78. h->last = (unsigned char *)h + sizeof(struct mp_node_s);
    79. }
    80. }
    81. static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {
    82. unsigned char *m;
    83. struct mp_node_s *h = pool->head;
    84. size_t psize = (size_t)(h->end - (unsigned char *)h);//小块的 头+数据部分 的大小
    85. int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize);//分配一个 小块
    86. if (ret) return NULL;
    87. struct mp_node_s *p, *new_node, *current;
    88. new_node = (struct mp_node_s*)m;
    89. new_node->end = m + psize;
    90. new_node->next = NULL;
    91. new_node->failed = 0;
    92. m += sizeof(struct mp_node_s);//添加头部
    93. m = mp_align_ptr(m, MP_ALIGNMENT);//头部对齐
    94. new_node->last = m + size;
    95. current = pool->current;
    96. for (p = current; p->next; p = p->next) {
    97. if (p->failed++ > 4) {//如果failed > 4, 就漏过这个node(也就是说每个块,在查找块的时候只允许失败4次,失败4次后,让current指向该节点的下一个)
    98. current = p->next;
    99. }
    100. }
    101. p->next = new_node;
    102. pool->current = current ? current : new_node;
    103. return m;
    104. }
    105. static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {
    106. void *p = malloc(size);
    107. if (p == NULL) return NULL;
    108. size_t n = 0;
    109. struct mp_large_s *large;
    110. for (large = pool->large; large; large = large->next) {
    111. if (large->alloc == NULL) {
    112. large->alloc = p;
    113. return p;
    114. }
    115. if (n ++ > 3) break;
    116. }
    117. large = mp_alloc(pool, sizeof(struct mp_large_s));
    118. if (large == NULL) {
    119. free(p);
    120. return NULL;
    121. }
    122. large->alloc = p;
    123. large->next = pool->large;//链表节点头部插入节点
    124. pool->large = large;
    125. return p;
    126. }
    127. void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) {
    128. void *p;
    129. int ret = posix_memalign(&p, alignment, size);
    130. if (ret) {
    131. return NULL;
    132. }
    133. struct mp_large_s *large = mp_alloc(pool, sizeof(struct mp_large_s));
    134. if (large == NULL) {
    135. free(p);
    136. return NULL;
    137. }
    138. large->alloc = p;
    139. large->next = pool->large;
    140. pool->large = large;
    141. return p;
    142. }
    143. //API
    144. void *mp_alloc(struct mp_pool_s *pool, size_t size) {
    145. unsigned char *m;
    146. struct mp_node_s *p;
    147. if (size <= pool->max) {
    148. p = pool->current;
    149. do {
    150. m = mp_align_ptr(p->last, MP_ALIGNMENT);//内存对齐
    151. if ((size_t)(p->end - m) >= size) {//block中要有足够空间能放下当前的size
    152. p->last = m + size;
    153. return m;
    154. }
    155. p = p->next;//查找有空间可以装下的block
    156. } while (p);
    157. return mp_alloc_block(pool, size);//没有block可以装了,那就创建一个小块,并分配
    158. }
    159. return mp_alloc_large(pool, size);//分配一个大块
    160. }
    161. void *mp_nalloc(struct mp_pool_s *pool, size_t size) {
    162. unsigned char *m;
    163. struct mp_node_s *p;
    164. if (size <= pool->max) {
    165. p = pool->current;
    166. do {
    167. m = p->last;
    168. if ((size_t)(p->end - m) >= size) {
    169. p->last = m+size;
    170. return m;
    171. }
    172. p = p->next;
    173. } while (p);
    174. return mp_alloc_block(pool, size);
    175. }
    176. return mp_alloc_large(pool, size);
    177. }
    178. void *mp_calloc(struct mp_pool_s *pool, size_t size) {
    179. void *p = mp_alloc(pool, size);
    180. if (p) {
    181. memset(p, 0, size);
    182. }
    183. return p;
    184. }
    185. //API 只释放大块
    186. void mp_free(struct mp_pool_s *pool, void *p) {
    187. struct mp_large_s *l;
    188. for (l = pool->large; l; l = l->next) {
    189. if (p == l->alloc) {
    190. free(l->alloc);
    191. l->alloc = NULL;
    192. return ;
    193. }
    194. }
    195. }
    196. int main(int argc, char *argv[]) {
    197. int size = 1 << 12;
    198. struct mp_pool_s *p = mp_create_pool(size);
    199. int i = 0;
    200. for (i = 0;i < 10;i ++) {
    201. void *mp = mp_alloc(p, 512);
    202. // mp_free(mp);
    203. }
    204. //printf("mp_create_pool: %ld\n", p->max);
    205. printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32));
    206. //printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large);
    207. int j = 0;
    208. for (i = 0;i < 5;i ++) {
    209. char *pp = mp_calloc(p, 32);
    210. for (j = 0;j < 32;j ++) {
    211. if (pp[j]) {
    212. printf("calloc wrong\n");
    213. }
    214. printf("calloc success\n");
    215. }
    216. }
    217. //printf("mp_reset_pool\n");
    218. for (i = 0;i < 5;i ++) {
    219. void *l = mp_alloc(p, 8192);
    220. mp_free(p, l);
    221. }
    222. mp_reset_pool(p);
    223. //printf("mp_destory_pool\n");
    224. for (i = 0;i < 58;i ++) {
    225. mp_alloc(p, 256);
    226. }
    227. mp_destory_pool(p);
    228. return 0;
    229. }

     

     推荐一个不错的学习网站 C/C++后台高级服务器https://ke.qq.com/course/417774?flowToken=1010783

  • 相关阅读:
    卷积神经网络的结构组成与解释(详细介绍)
    10个好用的Mac数据恢复软件推荐—恢复率高达99%
    C2基础设施威胁情报对抗策略
    Python中如何使用__slots__限制对象属性来节约内存
    前端实现SuperMap iServer服务增(发布)删改查和刷新的方法.md
    《护理教育学》名词解释、简答题、问答题汇总
    当transcational遇上synchronized
    【LeetCode】5. 最长回文子串
    中国钨粉行业市场研究与投资预测报告(2022版)
    Mysql 单行处理函数打字练习—— 让你熟悉必要的函数,提高查询效率
  • 原文地址:https://blog.csdn.net/kakaka666/article/details/126961712