内存池的作用:
- 减少应用程序使用和释放的内存块过多并且过于频繁导致的内存分配成本过高。
- 频繁分配内存容易导致内存泄漏和内存碎片。
- 由内存池统一分配内存,提高内存分配性能。
swoole里的三个有关内存的结构体:
// swMemoryGlobal管理所有分配的内存page,是一个单链表。
typedef struct _swMemoryGlobal
{
uint8_t shared; // 是否进程间共享,shared = 1时,底层内存分配使用mmap生成一块共享内存区。
uint32_t pagesize; // 每个内存页大小,为2*1024*1024
swLock lock; // 互斥锁
swMemoryGlobal_page *root_page; // 指向第一个swMemoryGlobal_page指针
swMemoryGlobal_page *current_page; // 指向当前swMemoryGlobal_page指针
uint32_t current_offset;
} swMemoryGlobal;
// 每个内存页的结构体
typedef struct _swMemoryGlobal_page
{
struct _swMemoryGlobal_page *next; // 指向下一个内存页结构体
char memory[0]; // 柔性数组,记录申请内存后的内存地址
} swMemoryGlobal_page;
// 内存池
typedef struct _swMemoryPool
{
void *object; // swMemoryGlobal_page的memory
void* (*alloc)(struct _swMemoryPool *pool, uint32_t size); // 分配内存的函数指针
void (*free)(struct _swMemoryPool *pool, void *ptr); // 释放内存的函数指针
void (*destroy)(struct _swMemoryPool *pool); // 销毁内存的函数指针
} swMemoryPool;
// swoole的内存分配就是在swMemoryPool进行的。
void swoole_init(void) {
// 省略
//init global shared memory
// SW_GLOBAL_MEMORY_PAGESIZE = 2 * 1024 * 1024,每个内存页大小
// shared = 1,这次创建的是共享内存
SwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1);
if (SwooleG.memory_pool == NULL)
{
printf("[Core] Fatal Error: global memory allocation failure");
exit(1);
}
// 省略
}
swMemoryPool* swMemoryGlobal_new(uint32_t pagesize, uint8_t shared)
{
// 全局内存管理器
swMemoryGlobal gm, *gm_ptr;
// pagesize不能大于2 * 1024 * 1024
assert(pagesize >= SW_MIN_PAGE_SIZE);
// 初始化全局内存管理器 gm
bzero(&gm, sizeof(swMemoryGlobal));
gm.shared = shared; // 标记该内存为共享内存
gm.pagesize = pagesize; // 标记该内存页大小
swMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm);
if (page == NULL)
{
return NULL;
}
if (swMutex_create(&gm.lock, shared) < 0)
{
return NULL;
}
gm.root_page = page; // 第一个swMemoryGlobal_page指针
// 该全局内存管理器目前管理的内存空闲空间
gm_ptr = (swMemoryGlobal *) page->memory;
// 由于swMemoryGlobal的信息需要保存到page->memory中,gm.current_offset内存空间偏移需要
// 加上这一部分,避免内存分配出现问题。
gm.current_offset += sizeof(swMemoryGlobal);
// 由于swMemoryPool内存池信息也要保存到page->memory中,gm.current_offset内存空间偏移需要
// 加上这一部分,避免内存分配出现问题。
swMemoryPool *allocator = (swMemoryPool *) (page->memory + gm.current_offset);
gm.current_offset += sizeof(swMemoryPool);
// 这里其实已经做了将swMemoryPool内存池信息也要保存到page->memory的步骤
allocator->object = gm_ptr; // 内存池具体管理的内存块
allocator->alloc = swMemoryGlobal_alloc; // 分配内存的函数
allocator->destroy = swMemoryGlobal_destroy; // 销毁内存的函数指针
allocator->free = swMemoryGlobal_free; // 释放内存的函数指针
// 这里将全局内存管理器信息保存保存在gm_ptr指向的内存空间,也就是page->memory
// 这就是gm.current_offset += sizeof(swMemoryGlobal)这步骤的原因
memcpy(gm_ptr, &gm, sizeof(gm));
return allocator;
}
static swMemoryGlobal_page* swMemoryGlobal_new_page(swMemoryGlobal *gm)
{
// sw_shm_malloc用mmap创建共享内存
// mmap该函数可以在unix网络编程卷2进程间通信找到详细说明
// sw_malloc 也就是malloc
swMemoryGlobal_page *page = (swMemoryGlobal_page *)((gm->shared == 1) ? sw_shm_malloc(gm->pagesize) : sw_malloc(gm->pagesize));
if (page == NULL)
{
return NULL;
}
// 由于内存池的page可能不够,因此需要分配多次的page
// 上一部分说道,swMemoryGlobal_page 是单链表
bzero(page, gm->pagesize);
page->next = NULL;
if (gm->current_page != NULL)
{
gm->current_page->next = page;
}
gm->current_page = page;
gm->current_offset = 0;
return page;
}
void* sw_shm_malloc(size_t size)
{
// 内存对齐
size = SW_MEM_ALIGNED_SIZE(size);
// 共享内存结构体
swShareMemory object;
void *mem;
// 由于mmap分配的共享内存的信息也需要记录在分配的内存空间中,因此实际分配的内存大小
// 为 size += sizeof(swShareMemory)
size += sizeof(swShareMemory);
mem = swShareMemory_mmap_create(&object, size, NULL);
if (mem == NULL)
{
return NULL;
}
else
{
// mmap分配的共享内存的信息也需要记录在分配的内存空间中
memcpy(mem, &object, sizeof(swShareMemory));
// 只返回空闲的内存空间
return (char *) mem + sizeof(swShareMemory);
}
}