内存池主要解决的问题避免频繁的创建和销毁内存。内存的申请和释放是很低效的。避免内存碎片内存碎片当你malloc了16字节的内存然后释放掉再malloc了8字节的内存那么还有8字节内存未被使用如果再申请4字节内存那还剩下4字节内存。可以一直划分直到所剩的内存空间太小而分配不出内存这样就会有小部分内存空间无法利用便叫做内存碎片。工作原理预申请大块内存池子创建时一次性向系统申请一大块连续内存。维护空闲块链表内存池内部会维护一个数据结构如链表记录当前哪些内存块是空闲的、哪些已被占用。自定义分配与释放分配当程序请求内存时内存池从空闲链表中取出一块返回而不调用系统函数。释放程序归还内存时内存池将其放回空闲链表而不是还给操作系统除非整个池子被销毁。内存池一般分为固定大小的内存池和大小不固定的内存池。一般不同的项目需求不同所以使用的内存池也大不相同。而线程池则具有很强的适配性各个项目的线程池都相差不大。固定大小的内存池一次申请大块的内存。把整个大块内存分成固定大小的小块。每一小块的前8个字节(一个指针8字节)存放下一个空闲小块的地址。这样可以重复利用已经申请然后释放后的内存。结构体设计typedef struct mempool_s { int block_size; // 每个块的大小 int free_count; // 空闲块数量 char *free_ptr; // 指向第一个空闲块 char *mem; // 指向实际分配的内存起始地址 } mempool_t;char *ptr m-free_ptr; for (i 0;i m-free_count;i ) { *(char **)ptr ptr size; ptr size; } *(char **)ptr NULL;指针占用大小8字节char *ptr m-free_ptr; 表示 ptr中就是 m-free_ptr的地址char ** ptr 表示 m-free_ptr前8个字节的地址*(char **)ptr 表示 m-free_ptr前8个字节*(char **)ptr ptr size; 表示 m-free_ptr前8个字节存放下一个小块的地址这段代码表示将每一个小块用指针连接起来。void mp_free(mempool_t *m, void *ptr) { *(char **)ptr m-free_ptr; // 要释放的小块 指向原来的空闲块 m-free_ptr (char *)ptr; // 空闲块指向这个要释放的小块 m-free_count ; }那这个要释放的小块内存为什么不清零如果直接往里面放数据会不会冲突不需要用户申请的时候有责任清零。把清零任务交给用户。只需要更新一下指针。如果申请的这页内存用完后MEM_PAGE_SIZE和下一页之间是不是也没有关联没关联。// 固定大小块内存池 #include stdio.h #include stdlib.h // #define MEM_PAGE_SIZE 4096 typedef struct mempool_s { int block_size; // 每个块的大小 int free_count; // 空闲块数量 char *free_ptr; // 指向第一个空闲块 char *mem; // 指向实际分配的内存起始地址 } mempool_t; int mp_init(mempool_t *m, int size) { if (!m) return -1; if (size 16) size 16; // 设置最小块16字节 m-block_size size; m-mem (char *)malloc(MEM_PAGE_SIZE); if (!m-mem) return -1; m-free_ptr m-mem; m-free_count MEM_PAGE_SIZE / size; int i 0; char *ptr m-free_ptr; for (i 0;i m-free_count;i ) { *(char **)ptr ptr size; ptr size; } *(char **)ptr NULL; return 0; } void mp_dest(mempool_t *m) { if (!m || !m-mem) return ; free(m-mem); // 直接释放整块内存 } void *mp_alloc(mempool_t *m) { if (!m || m-free_count 0) return NULL; void *ptr m-free_ptr; // *(char **)ptr 表示m-free_ptr前8个字节即下一小块的地址 m-free_ptr *(char **)ptr; // 指向下一个空闲块 m-free_count --; return ptr; } /* 问那这个要释放的小块内存需要清零吗 如果直接往里面放数据会不会冲突 不需要也不会。 把清零任务交给用户。只需要更新一下指针。 如果申请的这页内存用完后MEM_PAGE_SIZE和下一页之间是不是也没有关联 没有关联。 */ void mp_free(mempool_t *m, void *ptr) { *(char **)ptr m-free_ptr; // 要释放的小块 指向原来的空闲块 m-free_ptr (char *)ptr; // 空闲块指向这个要释放的小块 m-free_count ; } int mp_get_count(mempool_t *m) { return m-free_count; } int main() { mempool_t m; mp_init(m, 32); // 每一小块内存32B void *p1 mp_alloc(m); printf(1: mp_alloc: %p\n, p1); void *p2 mp_alloc(m); printf(2: mp_alloc: %p\n, p2); void *p3 mp_alloc(m); printf(3: mp_alloc: %p\n, p3); void *p4 mp_alloc(m); printf(4: mp_alloc: %p\n, p4); mp_free(m, p2); void *p5 mp_alloc(m); printf(5: mp_alloc: %p\n, p5); return 0; }可变大小快内存池每一个节点具有不同大小的内存然后再用一个结构体管理所有的节点。typedef struct mp_node_s { char *free_ptr; // 当前节点的空闲位置指针 char *end; // 当前节点的结束位置 struct mp_node_s *next; // 指向下一个节点 } mp_node_t; typedef struct mp_pool_s { struct mp_node_s *first; // 第一个节点 struct mp_node_s *current; // 当前使用的节点 int max; // 每个节点的大小 } mp_pool_t;#include stdio.h #include stdlib.h #define MEM_PAGE_SIZE 4096 typedef struct mp_node_s { char *free_ptr; // 当前节点的空闲位置指针 char *end; // 当前节点的结束位置 struct mp_node_s *next; // 指向下一个节点 } mp_node_t; typedef struct mp_pool_s { struct mp_node_s *first; // 第一个节点 struct mp_node_s *current; // 当前使用的节点 int max; // 每个节点的大小 } mp_pool_t; int mp_init(mp_pool_t *m, int size) { if (!m) return -1; void *addr malloc(size); // 4096 mp_node_t *node (mp_node_t*)addr; // 空闲起始位置跳过节点头结构体 node-free_ptr (char*)addr sizeof(mp_node_t); node-end (char*)addr size; // 结束位置 node-next NULL; m-first node; m-current node; m-max size; return 0; } void mp_dest(mp_pool_t *m) { if (!m) return ; while (m-first) { void *addr m-first; mp_node_t *node (mp_node_t*)addr; m-first node-next; free(addr); } return ; } void *mp_alloc(mp_pool_t *m, int size) { //size (4096-sizeof(mp_node_t)) void *addr m-current; mp_node_t *node (mp_node_t*)addr; // 取出当前节点的头部结构体 do { if (size (node-end - node-free_ptr)) { // 当前节点内存足够 char *ptr node-free_ptr; node-free_ptr size; return ptr; } node node-next; // 当前节点内存不够换下一个节点 } while (node); // new node // 现有的节点内存都不够 申请一个新节点 addr malloc(m-max); // 4096 node (mp_node_t*)addr; node-free_ptr (char*)addr sizeof(mp_node_t); node-end (char*)addr m-max; // 头插法 node-next m-current; // 插入到current之前而不是current的next m-current node; char *ptr node-free_ptr; node-free_ptr size; return ptr; } void mp_free(mp_pool_t *m, void *ptr) { } int main() { mp_pool_t m; mp_init(m, MEM_PAGE_SIZE); void *p1 mp_alloc(m, 16); printf(1: mp_alloc: %p\n, p1); void *p2 mp_alloc(m, 32); printf(2: mp_alloc: %p\n, p2); void *p3 mp_alloc(m, 64); printf(3: mp_alloc: %p\n, p3); void *p4 mp_alloc(m, 128); printf(4: mp_alloc: %p\n, p4); void *p5 mp_alloc(m, 256); printf(5: mp_alloc: %p\n, p5); mp_dest(m); }