优化资源利用,用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.hqwc.cn/news/689078.html

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

相关文章

Linux学习笔记7---仿STM32自建寄存器库

为了开发方便&#xff0c;ST 官方为 STM32F103 编写了一个叫做 stm32f10x.h 的文件&#xff0c;在这个文件里面定义了 STM32F103 所有外设寄存器。而有些芯片是没有这种寄存器库的&#xff0c;在没有的情况下要学会自己建立一个寄存器库。NXP 官方并没有为 I.MX6UL 编写类似 st…

大模型微调之 在亚马逊AWS上实战LlaMA案例(十)

大模型微调之 在亚马逊AWS上实战LlaMA案例&#xff08;十&#xff09; 训练数据集格式 SageMaker JumpStart 目前支持域适应格式和指令调整格式的数据集。在本节中&#xff0c;我们指定两种格式的示例数据集。有关更多详细信息&#xff0c;请参阅附录中的数据集格式化部分。 …

嵌入式 - GPIO编程简介

An Introduction to GPIO Programming By Jeff Tranter Wednesday, June 12, 2019 编者按&#xff1a;本 2019 年博客系列是 ICS 最受欢迎的系列之一&#xff0c;现已更新&#xff08;2022 年 12 月&#xff09;&#xff0c;以确保内容仍然准确、相关和有用。 本博客是 Integr…

VBA_MF系列技术资料1-605

MF系列VBA技术资料1-605 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

机器学习-Numpy

机器学习-Numpy 如果一个人拒绝提高自己的思想觉悟&#xff0c;那么他只能处在弱小、可怜、凄惨的境地。 目录 机器学习-Numpy 1.Numpy&#xff1a;生成矩阵 做矩阵运算 1&#xff09;创建矩阵 ①使用列表创建 ②使用元组创建 2&#xff09;矩阵取值 3&#xff09;numpy…

Go实现树莓派读取at24c02 eeprom读写数据

步骤 启用i2c 参考 Go实现树莓派读取bh1750光照强度 代码 package mainimport ("fmt""periph.io/x/conn/v3/i2c" )type AT24C02Device struct {dev *i2c.Dev }func NewAT24C02Device(addr uint16, bus i2c.BusCloser) (*AT24C02Device, error) {var (d…

Springboot+Vue项目-基于Java+MySQL的影院订票系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

Linux——基础IO(1)

前言 铺垫&#xff1a;文件 1.之前我们讲过文件内容属性 磁盘中创建一个空文件也要占空间(就算内容为空&#xff0c;文件属性也占空间) 文件操作文件内容的操作文件属性的操作 有可能在操作文件的过程中既改变内容又改变属性 2.访问文件之前&#xff0c;都得先打开文件 修改文…

HTTP基础概念和HTTP缓存技术

什么是HTTP HTTP是超文本传输协议&#xff0c;主要分为三个部分&#xff1a;超文本、传输、协议。 超文本是指&#xff1a;文字、图片、视频的混合体。传输是指&#xff1a;点与点之间的信息通信。协议是指&#xff1a;通信时的行为规范或约定 HTTP常见字段 字段名 解释 例…

2024年人工智能数据报告

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

微火一文盘点:为何全域运营系统会成为创业新风口?

当前&#xff0c;微火全域运营已经成为创业的新风口&#xff0c;想要做微火全域运营服务商的创业者数量日益增多。据目前了解到的最新消息&#xff0c;微火全域运营系统的市场占有率已经超过了48%&#xff0c;并且还在持续不断地上涨中。 所谓微火全域运营系统&#xff0c;就是…

浅谈C++ overload(重载) override(覆盖) overwrite(重写)

目录 1. 名词辨析2 含义解析1 overload重载2 override覆盖3 overwrite重写 3 区别4 代码示例 1. 名词辨析 关于这3个名词的中文翻译&#xff1a; overload翻译为重载&#xff0c;基本是没有歧义的&#xff1b;override和overwrite的翻译&#xff0c;我在参考了cppreference中…