内存池(Memory Pool)

devtools/2024/11/14 22:03:48/

内存池(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


http://www.ppmy.cn/devtools/47164.html

相关文章

【AI大模型】Transformers大模型库(七):单机多卡推理之device_map

目录​​​​​​​ 一、引言 二、单机多卡推理之device_map 2.1 概述 2.2 自动配置&#xff0c;如device_map"auto" 2.3 手动配置&#xff0c;如device_map"cuda:1" 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#x…

Unity 实现WebSocket 简单通信——客户端

创建连接 ClientWebSocket socket new ClientWebSocket(); string url $"ws://{ip}:{port}"; bool createUri Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri uri); if (createUri) {var task socket.ConnectAsync(uri, CancellationToken.None);task…

远程医疗平台如何连接医生和患者?

远程医疗平台&#xff0c;以其创新的信息技术手段&#xff0c;构筑了一个无视地理界限的医疗服务新体系&#xff0c;实现了医患之间的实时互动和诊疗服务。例如欣九康诊疗系统&#xff0c;通过一系列功能模块&#xff0c;有效连接了医生与患者&#xff0c;为两者提供了一个全面…

MFC设置窗口在Z轴上的位置

函数原型&#xff1a; BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags);返回值&#xff1a; 如果函数成功&#xff0c;则返回非零值&#xff1b;否则返回0。 参数&#xff1a; pWndInsertAfter&#xff1a;标识了在Z轴次…

Linux - 深入理解/proc虚拟文件系统:从基础到高级

文章目录 Linux /proc虚拟文件系统/proc/self使用 /proc/self 的优势/proc/self 的使用案例案例1&#xff1a;获取当前进程的状态信息案例2&#xff1a;获取当前进程的命令行参数案例3&#xff1a;获取当前进程的内存映射案例4&#xff1a;获取当前进程的文件描述符 /proc中进程…

【稳定检索/投稿优惠】2024年智慧金融与财务管理国际会议(SFFM 2024)

2024 International Conference on Smart Finance and Financial Management 2024年智慧金融与财务管理国际会议 【会议信息】 会议简称&#xff1a;SFFM 2024 截稿时间&#xff1a;以官网为准 大会地点&#xff1a;中国广州 会议官网&#xff1a;www.iacsffm.com 会议邮箱&am…

Django与MySQL:配置数据库的详细步骤

文章目录 Django-MySQL 配置配置完执行数据迁移&#xff0c;如果报错: Error loading MySQLdb module&#xff0c; Django-MySQL 配置 # settings.pyDATABASES {# 默认配置sqlite3数据库# default: {# ENGINE: django.db.backends.sqlite3,# NAME: BASE_DIR / db.sqli…

实验11 OSPF协议配置

实验11 OSPF协议配置 一、OSPF单区域配置&#xff08;一&#xff09;原理描述&#xff08;二&#xff09;实验目的&#xff08;三&#xff09;实验内容&#xff08;四&#xff09;实验配置&#xff08;五&#xff09;实验步骤 二、OSPF多区域配置&#xff08;一&#xff09;原理…