在编程中,为了避免由于频繁的malloc/free产生内存碎片,通常会在程序中实现自己的内存管理模块,即内存池。内存池的原理:程序启动时为内存池申请一块较大的内存,在程序中使用内存时,都由内存池进行分配,不再使用的内存交给内存池回收,用于再次分配。内存池一般会有如下的接口:memory_pool_init, memory_pool_malloc, memory_pool_free 和 memory_pool_destroy。本文实现了一个简单的内存池,仅用于基础学习。
memory_pool.h #ifndef __MEMORY_POOL_H__ #define __MEMORY_POOL_H__ #define MAX_POOL_SIZE 1024 * 1024 #define BLOCK_SIZE 64 typedef struct memory_map_talbe { char *p_block; int index; int used; } Memory_Map_Table; typedef struct memory_alloc_table { char *p_start; int used; int block_start_index; int block_cnt; }Memory_Alloc_Table; typedef struct memory_pool { char *memory_start;//内存池起始地址, free整个内存池时使用 Memory_Alloc_Table *alloc_table; Memory_Map_Table *map_table; int total_size; int internal_total_size; int increment; int used_size; int block_size; int block_cnt; int alloc_cnt; } Memory_Pool; extern Memory_Pool *memory_pool_init(int size, int increment); extern void *Memory_malloc(Memory_Pool *pool, int size); extern void memory_free(Memory_Pool *pool, void *memory); extern void memory_pool_destroy(Memory_Pool *pool); #endif memory_pool.c: #include <stdio.h> #include <stdlib.h> #include <string.h> #include "memory_pool.h"
//获取内存映射表的位置 Memory_Map_Table *map_table_pos(Memory_Pool *pool) { Memory_Map_Table *p = (Memory_Map_Table *)(pool->memory_start + sizeof(Memory_Pool)); return p; }
//获取内存分配表的位置 Memory_Alloc_Table *alloc_talbe_pos(Memory_Pool *pool) { Memory_Alloc_Table *p = (Memory_Alloc_Table *)(pool->memory_start + sizeof(Memory_Pool) + sizeof(Memory_Map_Table) * (pool->block_cnt)); return p; }
//获得memory在位置 char *memory_pos(Memory_Pool *pool) { char *p = (char *)(pool->memory_start + sizeof(Memory_Pool) + (sizeof(Memory_Map_Table) + sizeof(Memory_Alloc_Table))* pool->block_cnt); return p; } Memory_Pool *memory_pool_init(int size, int increment) { char *p = NULL; char *p_memory = NULL; Memory_Pool *pool = NULL; Memory_Alloc_Table *alloc_table = NULL; Memory_Alloc_Table *p_alloc_table = NULL; Memory_Map_Table *map_table = NULL; Memory_Map_Table *p_map_table = NULL; int block_cnt = 0; int all_size = 0; int i = 0; if (size < 0 || size > MAX_POOL_SIZE) { printf("memory_pool_init(): Invalid size(%d)\n", size); return pool; } block_cnt = ((size + BLOCK_SIZE - 1) / BLOCK_SIZE); all_size = sizeof(Memory_Pool) + (sizeof(Memory_Map_Table) + sizeof(Memory_Alloc_Table)) * block_cnt + size; p = (char *)malloc(all_size); if (p == NULL) { perror("Malloc failed\n"); return pool; } memset(p, 0, all_size); pool = (Memory_Pool *)p; pool->block_cnt = block_cnt; pool->block_size = BLOCK_SIZE; pool->increment = increment; pool->internal_total_size = BLOCK_SIZE * block_cnt; pool->total_size = size; pool->used_size = 0; pool->alloc_cnt = 0; pool->memory_start = p; p_memory = memory_pos(pool); map_table = map_table_pos(pool); for (i = 0; i < block_cnt; i++) { p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table)); p_map_table->index = 0; p_map_table->p_block = p_memory + i * BLOCK_SIZE; p_map_table->used = 0; } alloc_table = alloc_talbe_pos(pool); for (i = 0; i < block_cnt; i++) { p_alloc_table = (Memory_Alloc_Table *)((char *)alloc_table + i * sizeof(Memory_Alloc_Table)); p_alloc_table->block_cnt = 0; p_alloc_table->block_start_index = -1; p_alloc_table->p_start = NULL; p_alloc_table->used = 0; } printf("memory_pool_init: total size: %d, block cnt: %d, block size: %d\n", pool->total_size, pool->block_cnt, BLOCK_SIZE); return pool; } void *Memory_malloc(Memory_Pool *pool, int size) { char *p_start = NULL; int need_block_cnt = 0; Memory_Map_Table *map_table = NULL; Memory_Map_Table *p_map_table = NULL; Memory_Alloc_Table *alloc_table = NULL; Memory_Alloc_Table *p_alloc_table = NULL; int block_cnt = 0; int start_index = -1; int i = 0; if (size <= 0) { printf("Invalid size(%d)\n", size); return p_start; } if (size > pool->total_size) { printf("%d is more than total size\n", size); return p_start; } if (size > pool->total_size - pool->used_size) { printf("Free memory(%d) is less than allocate(%d)\n", pool->total_size - pool->used_size, size); return NULL; } need_block_cnt = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; map_table = map_table_pos(pool); start_index = -1; for (i = 0; i < pool->block_cnt; i++) { p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table)); if (p_map_table->used) { //printf("before alloc: map index: %d is used\n", i); block_cnt = 0; start_index = -1; continue; } if (start_index == -1) { start_index = i; //printf("start_index: %d\n", start_index); } block_cnt++; if (block_cnt == need_block_cnt) { break; } } if (start_index == -1) { printf("No available memory to used\n"); return NULL; } alloc_table = alloc_talbe_pos(pool); for (i = 0; i < pool->block_cnt; i++) { p_alloc_table = (Memory_Alloc_Table *)((char *)alloc_table + i * sizeof(Memory_Alloc_Table)); if (p_alloc_table->used == 0) { break; } p_alloc_table = NULL; } if (p_alloc_table == NULL) { return NULL; } p_map_table = (Memory_Map_Table *)((char *)map_table + sizeof(Memory_Map_Table) * start_index); p_alloc_table->p_start = p_map_table->p_block; p_alloc_table->block_start_index = p_map_table->index; p_alloc_table->block_cnt = block_cnt; p_alloc_table->used = 1; //printf("block cnt is %d\n", block_cnt); for (i = start_index; i < start_index + block_cnt; i++) { p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table)); //printf("map index: %d is used\n", i); p_map_table->used = 1; } printf("Alloc size: %d, Block: (start: %d, end: %d, cnt: %d)\n", size, start_index, start_index + block_cnt - 1, block_cnt); pool->alloc_cnt++; pool->used_size += size; return p_alloc_table->p_start; } void memory_free(Memory_Pool *pool, void *memory) { Memory_Map_Table *map_table = NULL; Memory_Map_Table *p_map_table = NULL; Memory_Alloc_Table *alloc_table = NULL; Memory_Alloc_Table *p_alloc_table = NULL; int i = 0; int block_start_index = 0; int block_cnt = 0; if (memory == NULL) { printf("memory_free(): memory is NULL\n"); return; } if (pool == NULL) { printf("Pool is NULL\n"); return; } alloc_table = alloc_talbe_pos(pool); for (i = 0; i < pool->alloc_cnt; i++) { p_alloc_table = (Memory_Alloc_Table *)((char *)(alloc_table) + i * sizeof(Memory_Alloc_Table)); if (p_alloc_table->p_start == memory) { block_start_index = p_alloc_table->block_start_index; block_cnt = p_alloc_table->block_cnt; } } if (block_cnt == 0) { return; } map_table = map_table_pos(pool); printf("Block: free: start: %d, end: %d, cnt: %d\n", block_start_index, block_start_index + block_cnt -1, block_cnt); for (i = block_start_index; i < block_start_index + block_cnt; i++) { p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table)); p_map_table->used = 0; } p_alloc_table->used = 0; pool->used_size -= block_cnt * BLOCK_SIZE; } void memory_pool_destroy(Memory_Pool *pool) { if (pool == NULL) { printf("memory_pool_destroy: pool is NULL\n"); return; } free(pool); pool = NULL; } main.c: #include <stdio.h> #include "memory_pool.h" #define LOOP 5 #define ALLOC_SIZE 8 int main(void) { Memory_Pool *pool = NULL; char *p1 = NULL; char *p2 = NULL; int i = 0; pool = memory_pool_init(1024, 512); if (pool == NULL) printf("memory pool init failed\n"); for (i = 0; i < 2; i++) { p1 = (char *)Memory_malloc(pool, ALLOC_SIZE); if (p1 == NULL) printf("Malloc failed\n"); else printf("Malloc success\n"); memory_free(pool, p1); } p1 = (char *)Memory_malloc(pool, 256); if (p1 == NULL) printf("Malloc failed\n"); else printf("Malloc success\n"); p2 = (char *)Memory_malloc(pool, 512); if (p1 == NULL) printf("Malloc failed\n"); else printf("Malloc success\n"); memory_free(pool, p1); p1 = (char *)Memory_malloc(pool, 256); if (p1 == NULL) printf("Malloc failed\n"); else printf("Malloc success\n"); memory_pool_destroy(pool); return 0; } Makefile: all: gcc main.c memory_pool.c -g
结果:
memory_pool_init: total size: 1024, block cnt: 16, block size: 64
Alloc size: 8, Block: (start: 0, end: 0, cnt: 1) Malloc success Block: free: start: 0, end: 0, cnt: 1 Alloc size: 8, Block: (start: 0, end: 0, cnt: 1) Malloc success Block: free: start: 0, end: 0, cnt: 1 Alloc size: 256, Block: (start: 0, end: 3, cnt: 4) Malloc success Alloc size: 512, Block: (start: 4, end: 11, cnt: 8) Malloc success Block: free: start: 0, end: 3, cnt: 4 Alloc size: 256, Block: (start: 0, end: 3, cnt: 4) Malloc success
内存池结构:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++ Memory_Pool +++ Memory_Map_Table +++ Memory_Alloc_Table +++ Memory++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Memory_Pool: 用于内存属性结构,用于整个内存池的管理
Memory_Map_Table: 内存映射表,是Memory的映射,用于管理Memory
Memory_Alloc_Table: 内存分配表,用于记录分配过的内存
Memory:实际用于分配的内存
说明:内存的分配是以块为单位的,向上取整,不足一块的按一块处理。
缺点:
1. 实际的可分配内存只是内存池中Memory表示的结构,其它的结构都是内存池管理用的,会造成内存浪费,而且是内存池越大,管理结构越大,浪费内存越多
2. 如果块大小设为64B, 而申请1B,会造成63B的浪费,所以申请内存最好是块大小的整数倍。
3. 当无内存可分配时,内存池不会自动增长