当前位置: 首页 > news >正文

c语言手撕内存池组件

内存池是什么?

        内存池(Memory Pool)是一种内存管理技术,它预先分配一大块内存,然后将其分成多个固定大小的小块。这些小块被组织起来,用于程序在运行期间频繁进行的内存分配和释放操作。内存池通过创建一个“池子”来管理这些小块,以便在需要时快速分配和回收内存,而不需要每次都调用系统的内存分配函数(如 malloc()free())。

        内存池的基本概念是分配一次,重复使用。通过预先分配一大块内存,程序可以在需要时从这个预先分配的内存中快速获取内存块,并在不使用时归还给池子,从而减少频繁的分配和释放操作。
 

使用内存池有什么好处?

  1. 提高性能

            系统的内存分配函数(如 malloc() 和 free())在操作系统级别会涉及复杂的管理操作,尤其是在高频率分配和释放内存时,开销可能很大。
            内存池通过提前分配和复用内存块,减少了频繁的系统调用,从而显著提高性能。特别是在实时系统或需要处理大量小对象的系统中,内存池的性能提升尤为显著。 
  2. 减少内存碎片

            当使用系统的内存分配函数时,如果程序频繁分配和释放不同大小的内存块,可能会导致内存碎片化,即分散的小块可用内存,无法被有效利用。

            内存池通常管理的是大小固定的内存块,因此不会产生内存碎片问题。
  3. 防止内存泄漏

            在某些情况下,内存池可以帮助减少内存泄漏的风险。因为内存池的内存块是固定的,开发者不需要担心忘记释放特定的内存块,只需要在适当的时候释放整个内存池。

        在不同的业务场景下内存池通常也都不一样,在初学内存池阶段,我们实现一个固定块的内存池(在内存池里分配大小一样的小块)。下边是实现过程:

 

        如果释放某个内存块以后,我们下一个可以分配的内存块应该指向刚刚释放的那个内存块,问题是怎么找到它呢?可以通过二级指针实现链表,使每个小内存块的前边存放下一个可以分配的内存块的地址。

函数实现:

#include <stdio.h>
#include <stdlib.h>// 8K的大小
#define MEM_PAGE_SIZE		8192typedef 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;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;m->free_ptr = *(char **)ptr;m->free_count --;return ptr;
}//释放
void mp_free(mempool_t *m, void *ptr) {*(char **)ptr = m->free_ptr;m->free_ptr = (char *)ptr;m->free_count ++;
}int main() {mempool_t m;//分配大小为64字节的固定块mp_init(&m, 64);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;
}

相关文章:

  • 利用Puppeteer-Har记录与分析网页抓取中的性能数据
  • 网络安全 DVWA通关指南 DVWA Weak Session IDs(弱会话)
  • C++中数据类型的大小
  • 【spring中event】事件简单使用
  • 【MySQL】数据目录迁移
  • 前端 vue3 对接科大讯飞的语音在线合成API
  • 详细指南:如何有效解决Windows系统中msvcp140.dll丢失的解决方法
  • 【cache】浅析四种常用的缓存淘汰算法 FIFO/LRU/LFU/W-TinyLFU
  • spark计算引擎-架构和应用
  • git 基本原理
  • 【项目开发】跨专业合作平台实战(附源码)
  • 初学51单片机之I2C总线与E2PROM二
  • c语言基础作业
  • YOLO11关键改进与网络结构图
  • mysql学习教程,从入门到精通,SQL 修改表(ALTER TABLE 语句)(29)
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【RocksDB】TransactionDB源码分析
  • 【技术性】Search知识
  • Brief introduction of how to 'Call, Apply and Bind'
  • DOM的那些事
  • Gradle 5.0 正式版发布
  • java 多线程基础, 我觉得还是有必要看看的
  • MySQL几个简单SQL的优化
  • Promise初体验
  • python学习笔记-类对象的信息
  • QQ浏览器x5内核的兼容性问题
  • React系列之 Redux 架构模式
  • SQLServer之创建数据库快照
  • windows-nginx-https-本地配置
  • 阿里云Kubernetes容器服务上体验Knative
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 初识 beanstalkd
  • 从setTimeout-setInterval看JS线程
  • 对JS继承的一点思考
  • 给初学者:JavaScript 中数组操作注意点
  • 如何学习JavaEE,项目又该如何做?
  • 温故知新之javascript面向对象
  • 自制字幕遮挡器
  • Python 之网络式编程
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​2021半年盘点,不想你错过的重磅新书
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (35)远程识别(又称无人机识别)(二)
  • (C#)一个最简单的链表类
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (k8s)kubernetes集群基于Containerd部署
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • (转)VC++中ondraw在什么时候调用的