优化资源利用,用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.mzph.cn/news/836153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

环形链表(判断链表中是否有环)的讲解

一&#xff1a;题目 二&#xff1a;思路讲解 1&#xff1a;采用快慢指针的方法&#xff0c;一个fast指针一次移动两个节点&#xff0c;一个slow指针一次移动一个节点。 2&#xff1a;两个指针从头指针开始往后遍历&#xff0c;如果fast指针或者fast->next 有一个为空&…

5款可用于LLMs的爬虫工具/方案

5款可用于LLMs的爬虫工具/方案 Crawl4AI 功能: 提取语义标记的数据块为JSON格式&#xff0c;提供干净的HTML和Markdown文件。 用途: 适用于RAG&#xff08;检索增强生成&#xff09;、微调以及AI聊天机器人的开发。 特点: 高效数据提取&#xff0c;支持LLM格式&#xff0c;多U…

c++ 入门2

目录 五. 函数重载 1、参数类型不同 2、参数个数不同 3、参数类型顺序不同 C支持函数重载的原理--名字修饰(name Mangling&#xff09; 为什么C支持函数重载&#xff0c;而C语言不支持函数重载呢&#xff1f; 六. 引用 6.1 概念 6.2 引用特性 6.3 常引用 6.4 使用场景 …

数据结构之排序(上)

片头 嗨&#xff0c;小伙伴们&#xff0c;大家好&#xff01;我们今天来学习数据结构之排序&#xff08;上&#xff09;&#xff0c;今天我们先讲一讲3个排序&#xff0c;分别是直接插入排序、冒泡排序以及希尔排序。 1. 排序的概念及其应用 1.1 排序的概念 排序&#xff1a…

图书馆APP开发解决方案

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

百度云防护如何开启CC攻击防护

百度云防护的最重要的功能是可以CC攻击防护&#xff0c;针对CC攻击&#xff0c;百度云防护有被动的CC攻击拦截规则&#xff0c;也有主动自定义访问策略拦截。 今天百度云来教大家如何开启百度云防护的CC攻击防御功能。 1.进入防护模板功能-创建模板 2.开启CC攻击防御功能&…

李飞飞首次创业!

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 最近AI又有啥进展&#xff1f;一起看看吧~ 中国独角兽企业已达369家&#xff0c;六成以上与AI、芯片等硬科技赛道有关 2024中关村论坛“全球独角兽企业大会”上发布全新《中国独角兽企业发展报告&am…

探索互联网医院系统源码:开发在线药房小程序实战教学

今天&#xff0c;笔者将与大家一同深入探讨互联网医院系统的源码结构&#xff0c;并通过开发在线药房小程序的实战教学&#xff0c;为读者提供一种学习和理解这一领域的途径。 一、互联网医院系统源码解析 1.技术选型 互联网医院系统的开发离不开合适的技术选型&#xff0c;…

类和对象-Python-第二部分

师从黑马程序员 多态 抽象类&#xff08;接口&#xff09; #演示抽象类 class AC:def cool_wind(self):"""制冷"""passdef hot_wind(self):"""制热"""def swing_l_r(self):"""左右摆风""…

Cloudflare国内IP地址使用教程

Cloudflare国内IP地址使用教程 加速网站&#xff1a; 首先我们添加一个 A 记录解析&#xff0c;解析 IP 就是我们服务器真实 IP&#xff1a; 然后侧边栏 SSL/TLS - 自定义主机名&#xff1a; 回退源这里填写你刚刚解析的域名&#xff0c;保存后回退源状态为有效再来接下的操作…

第十二篇:数据库系统导论 - 探索数据管理的基石

数据库系统导论 - 探索数据管理的基石 1 引言 数据的力量&#xff1a;揭秘数据库系统的核心 在信息时代&#xff0c;数据无处不在&#xff0c;它们成为了企业和社会运作的基础。我们如何储存、检索、更新和维护这些数据&#xff0c;决定了我们能否从这些数据中获得力量。数据…

网络应用层

叠甲&#xff1a;以下文章主要是依靠我的实际编码学习中总结出来的经验之谈&#xff0c;求逻辑自洽&#xff0c;不能百分百保证正确&#xff0c;有错误、未定义、不合适的内容请尽情指出&#xff01; 文章目录 1.使用协议和序列化1.1.自定义协议&#xff0c;自定义序列化1.2.自…

【网络】网络基础

目录 一、前言 1.计算机网络背景 2.认识协议 二、网络协议初识 1.OSI七层模型 2.TCP/IP五层(或四层)模型 3.网络传输基本流程 4.数据包封装和分用 5.网络中的地址管理 1.IP地址 2.MAC地址 一、前言 1.计算机网络背景 网络之前&#xff0c;我们所有在电脑上的操作都是…

Spring Boot数据映射利器:MapperStruct vs. BeanUtils.copyProperties 一较高下

Spring Boot数据映射利器&#xff1a;MapperStruct vs. BeanUtils.copyProperties 一较高下 在 Spring Boot 应用程序中&#xff0c;常常需要在不同的 Java 对象之间进行数据拷贝。这种拷贝操作在开发中非常常见&#xff0c;比如将 DTO&#xff08;Data Transfer Object&#x…

5. 简单说一说uniapp中的语法吧

前言 如果你 知道Vue3并且对Vue3的语法有一定了解&#xff0c;请跳过这一章&#xff0c;由于后续项目主要是基于Vue3TypeScript&#xff0c;因此提前简单概述一些Vue3的基础语法~ 本文的目的是 期望通过对本文的阅读后能对Vue3的每个语法有一个简单的印象&#xff0c;至少要知…

二叉搜索数使用,底层原理及代码实现

1:二叉搜索树的定义 二叉搜索树的底层是一个二叉链表 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树 &#xff0c;或者是具有以下性质的二叉树 : 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所…

Redis-配置文件详解

Redis配置文件详解 units单位 配置大小单位&#xff0c;开头定义基本度量单位&#xff0c;只支持bytes&#xff0c;大小写不敏感。 INCLUDES Redis只有一个配置文件&#xff0c;如果多个人进行开发维护&#xff0c;那么就需要多个这样的配置文件&#xff0c;这时候多个配置 文…

docker安装向量数据库milvus

Miluvs Milvus 向量数据库能够帮助用户轻松应对海量非结构化数据(图片 / 视频 / 语音 / 文本)检索。 单节点 Milvus 可以在秒内完成十亿级的向量搜索,分布式架构亦能满足用户的水平扩展需求。 Milvus 向量数据库的应用场景包括:互联网娱乐(图片搜索 / 视频搜索)、新零售…

MATLAB基础—系统环境

1.MATLAB操作界面的组成 (1)MATLAB主窗口&#xff08;红色&#xff09; MATLAB主窗口是MATLAB的程序窗口&#xff0c;他除了嵌入一功能窗口外&#xff0c;主要包括功能区(1)&#xff0c;快速访问工具栏(2)&#xff0c;和当前文件夹工具栏(3)。 在功能区提供了三个选项卡&#…

浅析vue3自定义指令

vue3中可以像下面这样使用自定义指令。 这里我们只是定义了一个vFoucs变量&#xff0c;vue怎么知道这是一个指令呢&#xff1f; 这是因为约定大于配置&#xff0c;vue3中有这样一个约定&#xff08;截图来自官方文档&#xff09;&#xff1a; 注意这里说的是驼峰命令&#x…