优化资源利用,用C++内存池点亮编程之路

内存池介绍(Memory Pool):

它是一种内存分配方式,通过预先分配和复用内存块。

在真正使用内存之前,先申请一大块内存备用。当有新的内存需求时,就从内存池中分出一部分内存块,

若内存块不够再继续申请新的内存。如果我们不需要继续使用当前的内存块了 ,那么就还给内存池。
在这里插入图片描述

为什么要使用内存池

1、内存分配和释放通常涉及系统调用(如mallocfree | newdelete ),这些系统调用需要用户态和内核态之间的切换,频繁的系统调用开销较大。内存池是提前分配一块内存,后续我们的内申请都是从内存池中申请,不需要进行系统调用,从而降低了开销。

2、当程序频繁地申请和释放不同大小的内存块时,容易导致内存碎片。

3、传统的内存分配和释放通常涉及到复杂的算法和数据结构(如堆),以及可能的线程同步操作,这些都会消耗较多的CPU时间。而内存池通过预先分配并管理固定大小的内存块,可以大大简化这些操作,从而提高效率。

所以如果我们需要频繁分配和释放小块内存 或者 需要大量内存分配和释放,那么建议使用内存池来高效管理内存。

内存池的实现

内存池的相关接口、必要的属性。MemoryPool.h

#ifndef  _MEMORY_POOL_H_
#define _MEMORY_POOL_H_#include <iostream>
#include <string.h>
#include <vector>
#include <mutex>#define SIZE 1024 * 1024 class MemoryPool{
public:// 创建一个内存池, 单列模式,因为整个项目只需要一个内存池static MemoryPool& getInstance(){// 内存池默认大小是 1G  每块大小是1024字节static  MemoryPool memoryPool_( 1024* SIZE , 1024); // 1024 = 1KB * 1024  = 1mb *1024 = 1gb   return memoryPool_;}void *calloc_locate(size_t  size);  // 分配内存void delete_locate(void *ptr);   // 释放内存private:MemoryPool(size_t pool_size , size_t block_size);~MemoryPool();void init_memPool();   // 初始化内存池char *pool_;            // 内存池指针size_t pool_size_;    // 内存池的大小size_t block_size_;    // 每一块内存的大小std::vector<bool>use_block_;  // 每个内存块的使用情况std::mutex mutex_;     // 由于内存池是共享资源 , 所以在进行操作时要加锁
};#endif  // _MEMORY_POOL_H_

对相关接口进行实现 MemoryPool.cpp

#include "MemoryPool.h"MemoryPool::MemoryPool(size_t pool_size , size_t block_size){pool_size_ = pool_size;block_size_ = block_size;pool_ = new char[pool_size];std::cout<<"Memory Start: "<<static_cast<void *>(pool_)<<std::endl;init_memPool();}void MemoryPool::init_memPool(){// 内存分成的块数 = 内存池的总大小 / 每块的大小 use_block_.resize( pool_size_/block_size_ , false);}MemoryPool::~MemoryPool(){// 对整个内存池资源进行清理if( pool_ ){delete[] pool_;pool_ = nullptr;pool_size_ = 0;block_size_ = 0;use_block_.reserve(0);}}void *MemoryPool::calloc_locate(size_t  size){if( size > block_size_ ){  // 如果我们要分配的内存 ,大于我们每块的大小 return nullptr;}std::unique_lock<std::mutex> lock(mutex_);for(int i = 0 ;i < pool_size_ ;i += block_size_ ){if( !use_block_[i/block_size_]){  //当前块没有被使用过use_block_[i/block_size_] = true;std::cout<<"successful calloc_locate ptr: "<<static_cast<void *>(pool_ + i) <<std::endl;return pool_ + i ; }}std::cout<<"Failed calloc_locate ptr: "<<std::endl;// 内存池资源耗尽的情况return nullptr;}void MemoryPool::delete_locate(void *ptr){if( !ptr )  return ;if( ptr< pool_  || ptr > pool_ ){  // 需要释放的内存不在我们内存池范围内return ;}std::cout<<"delete ptr: "<<std::hex<<ptr<<std::endl;// 将指针进行对齐/*ptr = 100 -> 需要删除内存的起始位置pool_ -----> 内存池的起始位置    */std::unique_lock<std::mutex>lock;auto index = (reinterpret_cast<char *>(ptr) - pool_ )/block_size_;if( index >=0 && index <use_block_.size()){use_block_[index] = false;std::cout<<"Delete successful"<<std::endl;return ;}std::cout<<"Delete Failed"<<std::endl;
}

测试demo test.cpp

#include "MemoryPool.h"int main(int ,char **){MemoryPool &pool = MemoryPool::getInstance();// 分配1kbchar *ptr = reinterpret_cast<char *>(pool.calloc_locate(1024));if(!ptr)  return 1;std::cout<<"ptr: "<< static_cast<void *>(ptr)<<std::endl;char *ptr2 = reinterpret_cast<char *>(pool.calloc_locate(800));if(!ptr2)  return 1;std::cout<<"ptr: "<< static_cast<void *>(ptr2)<<std::endl;pool.delete_locate(ptr);return 0;
}
结果分析

在这里插入图片描述

程序开始 ,创建一个内存池的单例 ,并申请一大块内存 (1024 x 1024 x 1024)->1GB

每一块的大小是 1024 ,所以我们的内存池里面 ,有1024 x 1024 个内存块。

vectoruse_block_ 会记录每一个内存块的使用情况
如果被使用则标记为 true , 没有被使用就标记为 false

第一步我们从内存池中分配 1024 个字节,由于我们的每一个块大小是 1024 ,所以他会返回第一个内存块的起始地址。也就是内存池的起始位置。

第二步 ,我们再次从内存池中分配800个字节 , 此时第一个内存块已经被使用 ,并且每一个块大小是 1024,那么理所当然返回第二个内存块的起始地址。因为第二个内存块已经足够放下了。
在这里插入图片描述

数据分析:

第一个内存块返回的起始地址:0x7facb1767010.

第二个内存块返回的起始地址:0x7facb1767410

两者相差400,此时是 16 进制,那么我们将其转换成 10 进制 之后是 1024 为一个内存块的大小
(4 * 16 * 16 - 》 1024).

所以我们的内存池完美实现 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1419830.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

进程间通信(一)

IPC 在之前我们也有涉及到进程间通信的知识点&#xff0c;比如fork或exec或父进程读取子进程的退出码等&#xff0c;但是这种通信方式很有限&#xff0c;今天来学习进程间通信的其他技术——IPC&#xff08;InterProcess Communication&#xff09;。 IPC的方式通常有管道&…

JS代码随想录(一):数组

代码随想录 一、数组理论基础 二、LeetCode 704. 二分查找 三、LeetCode 27. 移除元素 四、LeetCode 977.有序数组的平方 五、LeetCode 209.长度最小的子数组 六、LeetCode 59.螺旋矩阵II 七、数组总结 一、数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 数组…

vue3+TS或JS, 实现粒子特效 @tsparticles/vue3

在跟着B站视频BV11s4y1a71T学习时&#xff0c;使用到了粒子效果&#xff0c;但是以下这种情况只适用于项目是基于typescript的写法&#xff0c;否则无法实现。 粒子效果 VUE3TStsparticles/vue31、安装2、main.ts 引入3、App.vue4、效果 VUE3JS非最新版1、安装低版本的vue3-pa…

生信人写程序1. Perl语言模板及配置

生物信息领域常用语言 个人认为&#xff1a;是否能熟悉使用Shell(项目流程搭建)R(数据统计与可视化)Perl/Python/Java…(胶水语言&#xff0c;数据格式转换&#xff0c;软件间衔接)三门语言是一位合格生物信息工程师的标准。 生物信息常用语言非常广泛&#xff0c;我常用的有…

5/11后面部分:+顺序排序+元素交换+计算每门课程的各种成绩+存放规律的数据 注意:一味的复制肯定要出问题,第2个的最后一部分有修改,注意观察

目录 第一个已经输出过一次&#xff1a; 第二个: 编程实现&#xff1a;程序功能是用起泡法对数组中n个元素按从大到小的顺序进行排序。 ​编辑的确出现了一些问题哦&#xff1a; ​编辑目前是可以运行&#xff0c;但AI不给我们通过&#xff1a; 最后还是我的代码获胜&#x…

SAP-CentralFinance - 会计核算中的组织要素 - 学习心得1

1. 定义SAP组织架构和理解各组织架构含义 组织结构遍布SAP 系统的所有重要功能范围。FI 中最重要的组织要素是公司代码。它是“财务会计”中的最小组织单位,可以为其编制自主式完整科目集供外部报告使用。其他重要的组织要素是利润中心业务范围和段。您可以为各个利润中…

基于Vue3与ElementUI Plus的酷企秀场景可视化DIY设计器探索(更新版)

一、引言 在当今数字化快速发展的时代&#xff0c;企业对于展示自身形象、产品细节以及提升客户体验的需求日益增强。酷企秀场景可视化DIY设计器&#xff0c;以其强大的功能和灵活的定制性&#xff0c;为企业提供了从VR全景展示到地图可视化、电子画册制作等一系列数字化解决方…

vue3使用高德地图

一、获取高德地图key和秘钥 1、注册高德开放平台账号 #高德地图开放平台地址 https://lbs.amap.com/2、创建应用和key(选择web端) 二、安装vuemap/vue-amap库 库地址&#xff1a;https://vue-amap.guyixi.cn/zh-cn/introduction/install.html // 安装核心库 npm install vu…

[ue5]编译报错:使用未定义的 struct“FPointDamageEvent“

编译报错&#xff0c;错误很多&#xff0c;但很明显核心问题是第一个&#xff1a;使用未定义的 struct“FPointDamageEvent“&#xff1a; 程序没有找到FPointDamageEvent的定义。 解决办法&#xff1a; 处理这类未定义都可以先F12&#xff0c;找到它的库位置&#xff0c;之后…

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接&#xff0c;因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

【Spring Boot】玩转基础 (一篇就够了)

目录 资源 项目地址 PS 一、新建 SpringBoot 项目 1.我这里连接了码云仓库 2.新建项目 2.1不用码云的的创建方式 2.2使用码云的创建方式 3.使用 Spring InitiaIizr 创建项目 4.选择基本 Dependencies 依赖项 5.设置项目与文件编码格式 UTF-8 6.观察我们的项目架构 7.…

论文 学习 Transformer : Attention Is All You Need

目录 概述&#xff1a; 对摘要的理解&#xff1a; 框架解析 按比例缩放的点积注意力 多头注意力机制 前馈神经网络与位置编码 概述&#xff1a; transformer 是一个encoder ——decoder 结构的用于处理序列到序列转换任务的框架&#xff0c;是第一个完全依赖自注意力机制…

软件测试基础知识必备之浅谈单元测试

什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&#xff0c;这里的最小可测试单元通常是指函数或者类。 单元测试都是以自动化的方式执行&#xff0c;所以在大量回归测试的场景下更能带来…

算法设计与分析 例题解答 解空间与搜索

1.请画出用回溯法解n3的0-1背包问题的解空间树和当三个物品的重量为{20, 15, 10}&#xff0c;价值为{20, 30, 25}&#xff0c;背包容量为25时搜索空间树。 答&#xff1a; 解空间树&#xff1a; 搜索空间树&#xff1a; 2. 考虑用分支限界解0-1背包问题 给定n种物品和一背包…

数据的均匀化分割算法(网格划分法、四叉树法(含C++代码))

数据的均匀化分割主要是指在分割过程中尽可能均匀地将数据点分布在各个子区域中&#xff0c;以保持数据分布的平衡和优化数据结构的性能。以下是几种可以实现数据均匀化分割的方法&#xff1a; 一. 网格划分法 1. 基本概念 虽然传统的网格划分法不是动态调整的&#xff0c;但通…

共享旅游卡免费旅游真实反馈,有图有真相?

新伙伴体验&#xff0c;云南昆大丽6天5晚品质双人游&#xff0c;真实反馈&#xff01;珠海伙伴蔡总&#xff0c;加入千益畅行共享旅游卡团队&#xff0c;自己亲自体验“云南昆大丽6天5晚品质双人游”真实反馈&#xff0c;分享全程内容截图&#xff0c;无半点虚假&#xff01; …

C语言例题36、判断一个数是否是回文数

题目要求&#xff1a;输入一个5位数&#xff0c;判断它是不是回文数。即12321是回文数 #include <stdio.h>int main() {int x;int ge, shi, qian, wan;printf("请输入一个5位数&#xff1a;");scanf("%d", &x);ge x % 10; //个sh…

Android ashmem 原理分析

源码基于&#xff1a;Andoird U Kernel-5.10 0. 简介 ashmem 称为匿名共享内存(Anonymous Shared Memory)&#xff0c;它以驱动程序的形式实现在内核空间中。它有两个特点&#xff1a; 能否辅助内存管理系统来有效地管理不再使用的内存块(pin / unpin)&#xff1b; 通过Bind…

ROS2 工作空间

文章目录 ROS2 工作空间创建工作空间自动安装依赖编译工作空间设置环境变量参考链接 ROS2 工作空间 工作空间可以简单理解为工程目录。 ROS 系统中一个典型的工作空间结构如图所示&#xff1a; dev_ws&#xff1a; 根目录&#xff0c;里面会有四个子目录&#xff08;子空间&a…

贪心算法----摆动序列

今日题目&#xff1a;leetcode376 点击跳转题目 观察样例2&#xff1a; 发现最长摆动序列都是极大值和极小值 再加上两个端点&#xff0c;那么我们保证每次都能选择到每个极值点&#xff0c;就能从局部最优推广全局最优了&#xff01; 但是还有一些细节情况需要注意&#xff…