内存池(Memory Pool)

发布于:2024-06-12 ⋅ 阅读:(157) ⋅ 点赞:(0)

内存池(Memory Pool)

内存池(Memory Pool)是一种内存管理技术,主要用于优化程序中动态内存分配和释放的效率,减少内存碎片,提高程序运行速度。以下是内存池的一些关键概念和工作原理介绍;

一、基本概念

内存池预先从操作系统申请一大块连续内存空间,并将其管理起来,当程序需要分配内存时,不再直接向操作系统请求,而是从内存池中快速分配一小块事先准备好的内存单元。当不再需要这些内存时,也不是直接归还给操作系统,而是归还给内存池,由内存池统一管理,适时或在程序结束时再归还给操作系统。

二、工作原理

预分配: 程序启动或初始化阶段,一次性向系统申请大量内存,形成内存池。
分割管理: 内存池中的内存会被分割成多个大小相等或不等的块(固定大小或可变大小内存池)。
分配: 当程序请求内存时,内存池快速分配一个合适的内存块给请求方,这个过程往往通过链表、位图等数据结构高效实现。
回收: 释放内存时,不是直接还给系统,而是归还给内存池,可能需要进行合并相邻空闲块的操作以减少碎片。
重用: 回收的内存块可以被后续的分配请求重用,减少了频繁的系统调用,提高了效率。

三、优缺点点
优点
  • 提高效率: 减少了系统调用的次数,内存分配和释放更快。
  • 减少碎片: 通过管理内存分配策略,可以有效减少内存碎片问题。
  • 可控性: 程序员可以更好地控制内存的使用,有利于内存泄漏的检测和防止。
  • 性能提升: 对于频繁分配和释放小块内存的场景尤其有效,如游戏、数据库、服务器等高性能应用。
缺点
  • 内存占用: 初始时预分配的内存可能会造成一定的内存浪费。
  • 实现复杂: 内存池的设计和实现相对复杂,需要考虑多种因素,如内存分配策略、内存碎片整理等。
  • 调试困难: 错误的内存管理可能会导致难以追踪的bug。
四、简单实现
  • 实现每次固定申请内存块的大小,每次内存可申请和释放
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MEM_POOL_SIZE 4096
typedef struct mempool_s {
    int block_size;    // 每个内存块的大小
    int free_count;    // 空闲内存块的数量
    char* free_ptr;     // 指向第一个空闲内存块的指针
    char* mem_ptr;      // 指向第一个内存块的指针
} mempool_t;

int mempool_init(mempool_t* pool, int block_size)
{
    // 初始化内存池
    if (pool == NULL)
        return -1;

    if (block_size <= 16) 
        block_size = 16;

    pool->block_size = block_size;
    pool->mem_ptr = (char*)malloc(MEM_POOL_SIZE);
    if (pool->mem_ptr == NULL)
        return -1;
    pool->free_ptr = pool->mem_ptr;
    pool->free_count = MEM_POOL_SIZE / block_size;


    // 初始化内存池中的内存块
    char* ptr = pool->free_ptr;
    for (int i = 0; i < pool->free_count; i++)
    {
    	//将当前内存块的首地址处存储下一个内存块的地址,通过 (char**)ptr 强制类型转换,
    	//将 ptr 转为指向 char* 类型的指针的指针,然后将 ptr + block_size 的值存入,形成一个单向链表结构。
        *(char**)ptr = ptr + block_size;     // 下一个内存块的指针
        ptr += block_size;                   // 移动当前内存块的指针 ptr 到下一个内存块的起始位置,即加上 block_size
    }
    *(char**)ptr = NULL;                    // 最后一个内存块的下一个地址设为 NULL,表示链表的结束

    return 0;
}

void* mempool_alloc(mempool_t* pool)
{
    // 分配一个内存块
    if (pool == NULL || pool->free_count <= 0)
        return NULL;

    void* ptr = pool->free_ptr;
    pool->free_ptr = *(char**)ptr;
    pool->free_count--;

    return ptr;
}

void mempool_dest(mempool_t* pool)
{
    // 销毁内存池
    if (pool == NULL || pool->mem_ptr == NULL)
        return;
    free(pool->mem_ptr);
}

void mempool_free(mempool_t* pool, void* ptr)
{
    // 释放一个内存块
    if (pool == NULL || ptr == NULL)
        return;
    //头插法
    *(char**)ptr = pool->free_ptr;
    pool->free_ptr = (char*)ptr;
    pool->free_count++;
}

int main()
{
    // 初始化内存池
    mempool_t pool;
    mempool_init(&pool, 16);
// 分配内存块
    void* ptr1 = mempool_alloc(&pool);
    memcpy(ptr1, "Hello, world!", 13);
    printf("ptr1 : %p,---%s\n", ptr1, (char*)ptr1);
    void* ptr2 = mempool_alloc(&pool);
    memcpy(ptr2, "你好!", 6);
    printf("ptr2 : %p,---%s\n", ptr2, (char*)ptr2);
    void* ptr3 = mempool_alloc(&pool);
    memcpy(ptr3, "再见", 6);
    printf("ptr3 : %p,---%s\n", ptr3, (char*)ptr3);
    // 释放内存块
    mempool_free(&pool, ptr1);
    void* ptr4 = mempool_alloc(&pool);
    printf("ptr4 : %p,---%s\n", ptr4, (char*)ptr4);
    // 再次释放内存块
    mempool_free(&pool, ptr2);
    void* ptr5 = mempool_alloc(&pool);
    printf("ptr5 : %p,---%s\n", ptr5, (char*)ptr5);
    // 销毁内存池
    mempool_dest(&pool);
    return 0;
}
  • 不固定内存大小,没有释放每次申请的空间,只有最有一个全部释放
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct mompool_node {
    char * free_ptr; // 指向内存池中空闲的内存
    char * end_ptr;   // 指向内存池的结束位置
    struct mompool_node * next; // 指向下一个内存池节点
} mompool_node_t;

typedef struct mompool {
    struct mompool_node * head; // 指向内存池链表的头部
    struct mompool_node * current; // 指向内存池链表的尾部
    int size; // 内存池页大小
} mompool_t;

int mompool_init(mompool_t * pool, int page_size) {
    // 初始化内存池
    if (pool == NULL)
        return -1;

    void * ptr = malloc(page_size);
    mompool_node_t *node = (mompool_node_t*)ptr;
    // node节点占用申请的内存空间
    node->free_ptr = (char*)ptr + sizeof(mompool_node_t);
    node->end_ptr = (char*)ptr + page_size;
    node->next = NULL;

    pool->head = node;;
    pool->current = node;
    pool->size = page_size;;
    return 0;
}

void * mompool_alloc(mompool_t * pool, int size) {
    // 从内存池中分配内存
    if (pool == NULL || size <= 0) return NULL;
    mompool_node_t* node = (mompool_node_t*)pool->current;

    do {
        if (node->end_ptr - node->free_ptr >= size) {
            char* ptr = node->free_ptr;
            node->free_ptr += size;
            return ptr;
        }
        node = node->next;

    } while (node);

    void * ptr = malloc(pool->size);
    node = (mompool_node_t*)ptr;
    node->free_ptr = (char*)ptr + sizeof(mompool_node_t);
    node->end_ptr = (char*)ptr + pool->size;
    
    // 头插法,加入内存池链表中
    node->next = pool->current;;
    pool->current = node;

    // 分配内存
    char* ret_ptr = node->free_ptr;
    node->free_ptr += size;

    return ret_ptr;
}

void mompool_destroy(mompool_t * pool) {
    // 销毁内存池
    if (pool == NULL) return;
    while (pool->head != NULL) {
        void * ptr = pool->head;
        mompool_node_t* node = (mompool_node_t*)ptr;
        pool->head = node->next;
        free(ptr);
    }
    return;
}

void mompool_free(mompool_t * pool, void * ptr) {
    // 将内存归还到内存池中
}


int main() {
    mompool_t mompool;
    mompool_init(&mompool, 4096);
    // 使用内存池分配内存
    void * ptr1 = mompool_alloc(&mompool, 16);
    // 内存越界
    memcpy(ptr1, "12345678901234567890", 20);
    printf("ptr1 : %p,---%s\n", ptr1, (char*)ptr1);
    void * ptr2 = mompool_alloc(&mompool, 32);
    memcpy(ptr2, "qwertyuiopasdfghjklzxcvbnmqwertyuiop", 36);
    printf("ptr2 : %p,---%s\n", ptr2, (char*)ptr2);
    printf("ptr1 : %p,---%s\n", ptr1, (char*)ptr1);
    memcpy(ptr1, "09876543210987654321", 20);
    printf("ptr1 : %p,---%s\n", ptr1, (char*)ptr1);
  	
    // sprintf(buffer, "%d", char),内有检测目标字符数组的大小,肯可能会出现内存越界的情况
    mompool_destroy(&mompool);
    return 0;
}

专属学习链接:https://xxetb.xetslk.com/s/36yiy3


网站公告

今日签到

点亮在社区的每一天
去签到