(内存池) 基于嵌入式指针的简单内存池

news/2024/3/3 15:56:15

文章目录

  • 前言
  • Code
    • MemoryPool
    • Test
  • 简单讲解
    • 应用方式
    • 参数
    • MemoryPool();
    • ~MemoryPool();
    • void* allocate();
    • void deallocate(void* p);
    • 测试效果
  • END

前言

内存池_百度百科 (baidu.com)

(Memory Pool)是一种内存分配方式,又被称为固定大小区块规划(fixed-size-blocks allocation)。通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。

内存池的实现方式多种多样,而本文仅实现一个简单的内存池,主要运用到嵌入式指针。

嵌入式指针,指的在数据单元中,用一部分空间保存某一块空间的地址信息。实现方式多种多样。

本示例的代码量非常少,但能够满足最基本的内存池需要的操作。

Code

MemoryPool

#include <cassert>
#include <cstdint>namespace lotus {
template <typename Type, size_t Element_Count>
class MemoryPool {using UNIT_TYPE = uint8_t;static constexpr size_t BLOCK_SIZE = sizeof(Type);private:void* m_buffer = nullptr;void* m_freeHead = nullptr;public:MemoryPool() {static_assert(sizeof(Type) >= sizeof(void*), "size is to small");m_buffer = malloc(BLOCK_SIZE * Element_Count);m_freeHead = nullptr;for (size_t i = 0; i < Element_Count; i += 1) {void* index = (UNIT_TYPE*)m_buffer + (i * BLOCK_SIZE);*(void**)(index) = m_freeHead;m_freeHead = index;}}~MemoryPool() {if (m_buffer) {free(m_buffer);m_buffer = nullptr;}if (m_freeHead) {m_freeHead = nullptr;}}public:void* allocate() {assert(m_freeHead);void* res = m_freeHead;m_freeHead = (void*)(*(void**)m_freeHead);return res;}void deallocate(void* p) noexcept {*(void**)p = m_freeHead;m_freeHead = p;}
};
}  // namespace lotus

Test

#include <iostream>namespace lotus {
template <typename Type, size_t Element_Count>
class MemoryPool {// ...
}
}int main() {using Tp = long long;lotus::MemoryPool<Tp, 100U> pool;{Tp* p1 = (Tp*)pool.allocate();*p1 = 123;printf("p = %p\t val = %lld\n", p1, *p1);Tp* p2 = (Tp*)pool.allocate();*p2 = 123456;printf("p = %p\t val = %lld\n", p2, *p2);pool.deallocate(p1);printf("after free p1\n");Tp* p3 = (Tp*)pool.allocate();printf("p = %p\t val = %lld\n", p3, *p3);}{printf(">> show near allocate()\n");for (int i = 0; i < 10; i += 1) {Tp* p = (Tp*)pool.allocate();printf("p = %p\t pval = %x\n", p, *p);}}
}

简单讲解

应用方式

将数据类型和类型单元数量作为模板参数确定

每次void* allocate()获取一个单元空间。

每次void deallocate(void* p)释放一个空间。请保证传入的是void* allocate()中获取的。


简单解释一下本文使用嵌入式指针的方式:

// p是一个指针,信息是一个地址值
void* p;
// pp = (void**)(p) 将p的信息强转为一个二级指针
// *pp 解引用,表示pp这块地址`的内容`
*(void**)(p);

参数

template <typename Type, size_t Element_Count>
// 数据类型
Type
// 最大申请个数
Element_Count// 保证每个内存单元式8字节
using UNIT_TYPE = uint8_t;
// type的大小作为一个数据块
static constexpr size_t BLOCK_SIZE = sizeof(Type);// 总内存指针
void* m_buffer = nullptr;
// 头指针,时刻指向可分配出的内存地址
void* m_freeHead = nullptr;

MemoryPool();

MemoryPool() {// 确保目前元素大小比指针大小大static_assert(sizeof(Type) >= sizeof(void*), "size is to small");// 一口气申请大内存m_buffer = malloc(BLOCK_SIZE * Element_Count);m_freeHead = nullptr;for (size_t i = 0; i < Element_Count; i += 1) {// 将大内存分块void* index = (UNIT_TYPE*)m_buffer + (i * BLOCK_SIZE);// !核心!嵌入式指针// 总体表现为每后一个空间都保存前一个空间的地址信息*(void**)(index) = m_freeHead;m_freeHead = index;}
}

~MemoryPool();

~MemoryPool() {// 释放内存if (m_buffer) {free(m_buffer);m_buffer = nullptr;}// 仅起视图代理作用,不掌管内存if (m_freeHead) {m_freeHead = nullptr;}
}

void* allocate();

void* allocate() {assert(m_freeHead);// 分配出去的地址void* res = m_freeHead;// 借助嵌入式指针,将head指向下一个空间m_freeHead = (void*)(*(void**)m_freeHead);return res;
}

void deallocate(void* p);

void deallocate(void* p) noexcept {// 嵌入式指针赋值// p的空间的地址保存head的信息*(void**)p = m_freeHead;// head更新为pm_freeHead = p;
}

测试效果

g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0

p = 0000000000751d98     val = 123    
p = 0000000000751d90     val = 123456 
after free p1
p = 0000000000751d98     val = 7675272
>> show near allocate()
p = 0000000000751d88     pval = 751d80
p = 0000000000751d80     pval = 751d78
p = 0000000000751d78     pval = 751d70
p = 0000000000751d70     pval = 751d68
p = 0000000000751d68     pval = 751d60
p = 0000000000751d60     pval = 751d58
p = 0000000000751d58     pval = 751d50
p = 0000000000751d50     pval = 751d48
p = 0000000000751d48     pval = 751d40
p = 0000000000751d40     pval = 751d38

第一部分可以看出,将p1归还后,再申请p3,获得的还是p1的地址。

第一部分可以看出,申请出空间的内容就是下一个空间的地址,如pval = 751d78的下一次申请为p = 0000000000751d78




END


http://www.ppmy.cn/news/1053324.html

相关文章

MySQL不停重启问题

MySQL不停的自动杀掉自动重启 看一下log日志 my.cnf 里配置的 log_error /var/log/mysqld.log vim /var/log/mysqld.log 报的错误只是 [ERROR] Cant start server: Bind on TCP/IP port: Address already in use [ERROR] Do you already have another mysqld server …

【C++STL基础入门】string迭代器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、迭代器是什么&#xff1f;二、string迭代器1.定义迭代器理解 2.通过迭代器遍历遍历方式1遍历方式2 3.迭代器失效4.涉及到的迭代函数1、begin()2、end()3、a…

嵌入式设备应用开发(数据标定和调试)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 嵌入式设备设计好,编译好固件烧入之后,接下来要走的一般就是数据标定和调试。就拿我们大家都比较熟悉的pid标定来说,第一步就是确定输入量和输出量;第二步,调试比例参数,直到…

Electron学习2 使用Electron-vue和Vuetify UI库

Electron学习2 使用Electron-vue和Vuetify UI库 一、Electron-vue简介二、安装yarn三、创建Electron-vue项目1. 关于 electron-builder2. 安装脚手架3. 运行4. 打包应用程序 四、background.js说明1. 引入模块和依赖&#xff1a;2. 注册协议&#xff1a;3. 创建窗口函数&#x…

solr快速上手:聚合分组查询|嵌套分组指南(十二)

0. 引言 solr作为搜索引擎经常用于各类查询场景&#xff0c;我们之前讲解了solr的查询语法&#xff0c;而除了普通的查询语法&#xff0c;有时我们还需要实现聚合查询来统计一些指标&#xff0c;所以今天我们接着来查看solr的聚合查询语法 1. 常用聚合查询语法 以下演示我们…

探索Vue生命周期钩子函数:从创生到销毁

Vue这个引领前端开发潮流的框架&#xff0c;其优雅的响应式数据绑定和组件式开发方式&#xff0c;使得它备受瞩目。然而&#xff0c;Vue的魅力绝不仅限于此&#xff0c;它还赋予开发者一组神奇的生命周期钩子函数&#xff0c;能够在组件的各个成长阶段插入自定义代码。本文将带…

iMX6ULL QT环境配置 | CMake在Linux下的交叉编译环境搭建及使用

习惯了使用cmake&#xff0c;再也不想回到手写makefile的年代了。相比手写makefile&#xff0c;使用cmake则像是实现了机动化&#xff0c;管理项目工程的编译变得很简单了。况且cmake很流行&#xff0c;linux下的很多软件源码包&#xff0c;很多也都使用了cmake的方式编译。因此…

【C语言督学训练营 第二十二天】C语言操作文件

文章目录 前言1.文件操作原理解析2.文件打开及关闭实战3.文件读写实战3.1 fread&fwrite3.2 fgets&fputs 4.文件位置指针偏移实战 前言 其实本篇博客标题应该是第二十三天&#xff0c;因为督学营讲的是二十三天的内容&#xff0c;至于为什么将第二十三天内容调为二十二…

MybatisPlus基础篇

一.入门案例 1.搭建SpringBoot环境 2.引入相关依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version></dependency><dependency><groupI…

基于Echarts的中国地图数据展示

概述 基于echarts的大数据中国地图展示&#xff0c;结合API定制&#xff0c;开发样式&#xff0c;监听鼠标事件&#xff0c;实现带参数路由跳转等自定义事件。 详细 一、概述 实际项目中大概率会遇到很多需要进行数据展示的地方&#xff0c;如折现图&#xff0c;柱状图等&…

如何通过人工智能和自动化提高供应链弹性?

全球供应链中的数字化转型已经引起了广泛关注&#xff0c;尽管在过去的十年中&#xff0c;这一话题被广泛讨论&#xff0c;但许多公司仍然对如何实现这一不明确的目标感到困惑。人们普遍认识到这种转变的重要性&#xff0c;而新冠疫情及其带来的巨大影响也为行业向数字化转型方…

Android学习之路(9) Intent

Intent 是一个消息传递对象&#xff0c;您可以用来从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信&#xff0c;但其基本用例主要包括以下三个&#xff1a; 启动 Activity Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity()&…

如何修改由 img 标签引入的 svg 图片颜色 (react环境)

网上试了好几个方法都不行&#xff0c;问了一下身边同事的处理方法&#xff0c;终于搞定了。话不多说&#xff0c;直接上代码&#xff1a; 此处是 jsx 中的图标引入 <img className{STYLE.contactIcon}onClick{() > {你的一些操作}} style{{WebkitMaskImage: url(${ite…

wazuh初探系列二 :Wazuh功能初步探知

目录 介绍 主动响应&#xff1a; 监控日志 "bin"目录用途&#xff1a; 告警信息&#xff1a; etc 目录中包含了以下主要的配置文件&#xff1a; ruleset&#xff1a;自带规则库&#xff0c;建议不改 rules目录: 解码器&#xff1a; 登录日志格式&#xff1a…

docker版jxTMS使用指南:使用jxTMS提供数据

本文讲解了如何jxTMS的数据访问框架&#xff0c;整个系列的文章请查看&#xff1a;docker版jxTMS使用指南&#xff1a;4.4版升级内容 docker版本的使用&#xff0c;请查看&#xff1a;docker版jxTMS使用指南 4.0版jxTMS的说明&#xff0c;请查看&#xff1a;4.0版升级内容 4…

第十七章、【Linux】认识系统服务

17.1 什么是daemon与服务&#xff08;service&#xff09; 系统为了某些功能必须要提供一些服务 &#xff08;不论是系统本身还是网络方面&#xff09;&#xff0c;这个服务就称为 service 。 但是 service 的提供总是需要程序的运行吧&#xff01;否则如何执行呢&#xff1f;…

docker 部署服务

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 [rootbogon ~]# docker pull mysql:5.6 [rootbogon ~]# docker pull owncloud [rootbogon ~]# docker run -itd --name mysql --env MYSQL_ROOT_PASSWORD123456 mysql:5.6 [rootbogon ~]# docker run -itd -…

CentOS Stream 9中安装MySQL的详细步骤

文章目录 卸载MySQL在线安装离线安装忘记密码 卸载MySQL 安装前先卸载系统上旧版本的 MySQL&#xff08;没有则跳过此步骤&#xff09; 查看已安装的MySQLrpm -qa | grep mysql卸载查询到的所有安装包rpm -e PackageName # 可批量删除删除my.cnf 查看/etc/my.cnf文件是否还存…

sentinel的基本使用

在一些互联网项目中高并发的场景很多&#xff0c;瞬间流量很大&#xff0c;会导致我们服务不可用。 sentinel则可以保证我们服务的正常运行&#xff0c;提供限流、熔断、降级等方法来实现 一.限流&#xff1a; 1.导入坐标 <dependency><groupId>com.alibaba.c…

Apache Hudi初探(二)(与flink的结合)--flink写hudi的操作(JobManager端的提交操作)

背景 在Apache Hudi初探(一)(与flink的结合)中&#xff0c;我们提到了Pipelines.hoodieStreamWrite 写hudi文件,这个操作真正写hudi是在Pipelines.hoodieStreamWrite方法下的transform(opName("stream_write", conf), TypeInformation.of(Object.class), operatorFa…
最新文章