【lesson2】定长内存池的实现

文章目录

  • 介绍
  • 定长内存池的设计
  • 定长内存池的实现
    • 需要成员变量
    • 需要的成员函数
    • 定长内存池结构
    • 定长内存池Delete(释放空间)的实现
    • 定长内存池New(申请空间)的实现
  • 定长内存池的实现完整版

介绍

作为程序员(C/C++)我们知道申请内存使用的是malloc,malloc其实就是一个通用的大众货,什么场景下都可以用,但是什么场景下都可以用就意味着什么场景下都不会有很高的性能,下面我们就先来设计一个定长内存池做个开胃菜,当然这个定长内存池在我们后面的高并发内存池中也是有价值的,所以学习他目的有两层,先熟悉一下简单内存池是如何控制的,第二他会作为我们后面内存池的一个基础组件。
在这里插入图片描述

定长内存池的设计

在这里插入图片描述
首先定长内存池的设计我们会向内存申请一大块空间,那么这么一大块空间我们肯定的知道在哪里,所以就用_memory指针指向该块空间
在这里插入图片描述
我们需要定义对象时,我们就只需要向内存池中的_memory申请一个对象大小的字节数就行了

我们被申请出去了一个或者多个对象大小的空间,那么一定会被还回来,所以我们就要对这些还回来的对象空间进行管理。
所以我们需要将这些还回来的对象,用单向链表管理起来
在这里插入图片描述
那么我们是如何用_freeList把一个一个对象链接起来的呢
我们可以用一个对象的前几个字节来存储下一个对象的地址。

定长内存池的实现

需要成员变量

在实现定长内存池之前我们要想,定长内存池需要哪些成员变量?

首先:我们向系统堆申请一定大小的空间,那么我们肯定要知道这块空间在哪里,所以第一个成员变量就是_memory指针,指向我们向系统申请的堆空间。
1._memory指针

其次:我们申请了对象就一定会被还回来,所以就要管理还回来的对象,所以第二个成员变量就是_freeList指针。
2._freeList指针

最后:我们_memory指向申请的堆空间如果一直被申请的话,那么申请的堆空间就一定会被使用殆尽,这时就需要向系统申请新的堆空间,那么我们该如何知道申请的堆空间是否被使用使用殆尽。所以就需要第三个成员变量_remainBytes记录剩余空间
3._remainBytes

需要的成员函数

除了成员变量我们还需要想定长内存池需要哪些成员函数?

首先:我们肯定需要一个成员函数,来为我们提供申请一个对象大小空间的窗口。
跟C++申请空间一样命名为New()。

最后:我们申请了一个对象大小的空间,那么最后肯定是要释放的,所以我们肯定要需要一个函数,来为我们提供释放空间的窗口
跟C++释放空间一样命名为Delete()。

定长内存池结构

在这里插入图片描述

定长内存池Delete(释放空间)的实现

Delete函数的逻辑很简单,我们只要把释放回来的对象空间,链接到_freeList即可,所以我们先实现Delete函数。

但是Delete我们也遇到了一个难题,还回来的是一个对象大小的空间啊,并不是一个对象啊。
那么我们如何把一个对象大小的空间链接到_freeList中呢?
这时我们就可以想到,我们可以一个对象大小空间的前4个字节存储指针的大小。
在这里插入图片描述
但是这时又遇到了一个问题,这个代码在32位平台下是可以的,但是在64位平台下指针是8个字节的该代码就不行了。
这时项目的高手就想到了一个办法。
在这里插入图片描述
在这里插入图片描述
(void**)在32位下解引用*(void**)是一个指针的指针,大小是4个字节。
(void**)在64位下解引用*(void**)也是一个指针的指针,大小是8个字节。

这时我们的问题就迎刃而解。

Delete函数的实现:

void Delete(T* obj){// 头插*(void**)obj = _freeList;_freeList = obj;}

定长内存池New(申请空间)的实现

New申请空间的步骤:
1.先查看_freeList是否有空闲的一个对象大小的空间,我们优先把还回来内存块对象,再次重复利用。

if (_freeList)
{void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;
}

2.如果_freeList没有空闲的对象空间,那么就向_memory要一块,对象大小的空间。
但是要的时候我们还得注意_memory指向的系统堆空间是否已经使用殆尽了?
如果已经使用殆尽了,我们得先向系统申请一块大的堆空间。
那么如何判断空间是否已经使用殆尽呢?

// 剩余内存不够一个对象大小时,则重新开大块空间
if (_remainBytes < sizeof(T))
{_remainBytes = 128 * 1024;//自己规定的向系统堆空间申请的空间大小//_memory = (char*)malloc(_remainBytes);_memory = (char*)SystemAlloc(_remainBytes >> 13);//Windows下脱离malloc申请大块空间if (_memory == nullptr){throw std::bad_alloc();}
}

这下我们就不考虑_memory指向的系统堆空间是否已经使用殆尽的问题了。
那么我们接下里就要向_memory要一个对象大小的空间。
那么如何要呢?
首先让obj指针_memory的首地址。
在这里插入图片描述
然后_memory += objSize,也就是_memory += 一个对象的大小。
在这里插入图片描述
这里大小可能也就明白了,为什么要把_memory定义成char的,因为char容易控制

然后我们把obj初始化,然后返给外层就可以了。

obj = (T*)_memory;//先把_memory的地址给obj
//然后计算要多少大小的空间,申请的空间大小 > 一个指针的大小 则用一个对象大小
//如果申请的空间大小 < 一个指针的大小 就用一个指针的大小
//因为我们要用对象的前几个字节存储地址,所以一个对象的大小必须 >= 一个指针
size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
_memory += objSize;
_remainBytes -= objSize;

New函数的实现:

T* New(){T* obj = nullptr;// 优先把还回来内存块对象,再次重复利用if (_freeList){void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;}else{// 剩余内存不够一个对象大小时,则重新开大块空间if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;//_memory = (char*)malloc(_remainBytes);_memory = (char*)SystemAlloc(_remainBytes >> 13);if (_memory == nullptr){throw std::bad_alloc();}}obj = (T*)_memory;size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);_memory += objSize;_remainBytes -= objSize;}// 定位new,显示调用T的构造函数初始化new(obj)T;return obj;}

定长内存池的实现完整版

#pragma once
#include <iostream>
#include <vector>
#include <time.h>
using std::cout;
using std::endl;#ifdef _WIN32
#include<windows.h>
#else
// 
#endif// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}template<class T>
class ObjectPool
{
public:T* New(){T* obj = nullptr;// 优先把还回来内存块对象,再次重复利用if (_freeList){void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;}else{// 剩余内存不够一个对象大小时,则重新开大块空间if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;//_memory = (char*)malloc(_remainBytes);_memory = (char*)SystemAlloc(_remainBytes >> 13);if (_memory == nullptr){throw std::bad_alloc();}}obj = (T*)_memory;size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);_memory += objSize;_remainBytes -= objSize;}// 定位new,显示调用T的构造函数初始化new(obj)T;return obj;}void Delete(T* obj){// 显示调用析构函数清理对象obj->~T();// 头插*(void**)obj = _freeList;_freeList = obj;}private:char* _memory = nullptr; // 指向大块内存的指针size_t _remainBytes = 0; // 大块内存在切分过程中剩余字节数void* _freeList = nullptr; // 还回来过程中链接的自由链表的头指针
};

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

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

相关文章

Zookeeper实现分布式队列

目录 Zookeeper分布式队列 普通方式实现 设计思路 具体实现 使用Curator实现 具体实现 注意事项 Zookeeper分布式队列 常见的消息队列有:RabbitMQ&#xff0c;RocketMQ&#xff0c;Kafka等。Zookeeper作为一个分布式的小文件管理系统&#xff0c;同样能实现简单的队列功…

【python】图形化开发pyqt6基本写法模板与基础控件属性方法整理

pyqt6的简介 首先呢Python有许多可以编写图形化界面的库&#xff0c;我们通常跟着教程的话最初会接触的tkinter&#xff0c;但是学习中会发现编写的图形化跟我们平常接触的软件有很大区别&#xff08;简单来说就是丑&#xff09;。 pyqt则是第三方库&#xff0c;在Python中算…

ETL怎么实现文件处理

在现代企业及各类组织的日常运作中&#xff0c;数据作为一种关键的信息资源&#xff0c;其管理和分析能力直接影响到决策效率与准确性。文件作为数据的主要载体&#xff0c;承载着从运营报告、客户记录、交易明细等各种类型的数据信息。这些海量且多样的文件数据在未经处理的情…

flask_django基于python的城市轨道交通公交线路查询系统vue

同时&#xff0c;随着信息社会的快速发展&#xff0c;城市轨道交通线路查询系统面临着越来越多的信息&#xff0c;因此很难获得他们对高效信息的需求&#xff0c;如何使用方便快捷的方式使查询者在广阔的海洋信息中查询&#xff0c;存储&#xff0c;管理和共享信息方面有效&…

C语言菜鸟入门·运算符(算数运算符,关系运算符,逻辑运算符,位运算符,赋值运算符,三目运算符)详细介绍

目录 ​编辑 1. 算术运算符 2. 关系运算符 3. 逻辑运算符 4. 位运算符 5. 赋值运算符 6. 杂项运算符 ↦ sizeof & 三元 6.1 sizeof&#xff08;&#xff09; 6.2 &取地址运算符 6.3 * 6.4 三目运算符 7. 运算符优先级 运算符是一种告诉编译器执行…

微信小程序canvas画布如何解决在for循环绘制图像显示不全的问题

如下所示&#xff0c;在for循环中绘制图像&#xff0c;由于onload函数加载图像是异步执行&#xff0c;会导致显示不全所有图片的问题。 for(var a0;a<2;a){ var img canvas.createImage()img.src product_image[items[a]]img.onload ()>{ctx.drawImage(el,0,h,i…

MySQL入门篇(7)-数据的插入、查询、更新与删除

MySQL是一种常用的关系型数据库管理系统&#xff0c;广泛应用于各种Web应用程序开发中。本博客将介绍MySQL数据库中数据的插入、查询、更新和删除操作&#xff0c;分别用到的是SQL语句中的INSERT、SELECT、UPDATE和DELETE语句。下面将详细介绍这些操作的用法及示例。 数据的插…

ElementUI组件:Link 文字链接

Link 文字链接 点击下载learnelementuispringboot项目源码 效果图 el-link.vue页面效果图 项目里el-link.vue文件代码 <script> export default {name: el_link }</script> <!--https://element.eleme.cn/#/zh-CN/component/link --> <template>&l…

TextView的setTextSize与xml中android:textSize属性值的对应关系

android中&#xff0c;对TextView设置文本字体大小&#xff0c;是通过在layout xml中设置android:textSize的属性值实现的&#xff0c;比如设置“24sp”&#xff0c;这里的sp是一种单位&#xff0c;其他可选的单位还有px&#xff0c;dip(dp)&#xff0c;pt&#xff0c;in&#…

嵌入式学习第十四天

1.结构体&#xff08;2&#xff09;: &#xff08;1&#xff09;结构体类型定义 &#xff08;2&#xff09;结构体变量的定义 &#xff08;3&#xff09;结构体元素的访问 &#xff08;4&#xff09;结构体的存储: 内存对齐: char 按照1字节对齐 …

C# OpenCvSharp DNN Gaze Estimation 视线估计

目录 介绍 效果 模型信息 项目 代码 frmMain.cs GazeEstimation.cs 下载 C# OpenCvSharp DNN Gaze Estimation 介绍 训练源码地址&#xff1a;https://github.com/deepinsight/insightface/tree/master/reconstruction/gaze 效果 模型信息 Inputs ----------------…

打破边界:如何配置 Nginx 反向代理以实现跨源CORS访问请求支持

背景知识 了解跨源访问的知识可参见笔者之前博客 flutter开发web应用网络请求后台失败--记录遇到的跨源资源共享问题-CSDN博客 本文环境 笔者的服务器为CentOS7.9&#xff0c;配置了宝塔面板&#xff0c;同时搭建了Wordpress网站&#xff0c;希望利用Wordpress的媒体库功能…

医院安全(不良)事件报告系统源码,不良事件处理的全过程管理,实现11大类不良事件类型的报告上报、流转审批、跟踪改进及统计分析功能。

医院安全&#xff08;不良&#xff09;事件报告系统源码&#xff0c;不良事件上报系统源码&#xff0c;PHP源码 医院安全&#xff08;不良&#xff09;事件报告系统提供11大类不良事件的上报、事件审核处理、时间按分析、事件跟踪与持续改进&#xff0c;事件提醒、权限控制、外…

聊聊DoIP吧

DoIP是啥? DoIP代表"Diagnostic over Internet Protocol",即互联网诊断协议。它是一种用于在车辆诊断中进行通信的网络协议。DoIP的目标是在现代汽车中实现高效的诊断和通信。通过使用互联网协议(IP)作为通信基础,DoIP使得诊断信息能够通过网络进行传输,从而提…

React通用后台模板

一. 项目初始化 1. 创建项目 环境 npm init vite 打开package.json,参考以下各模块版本: "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^7.2.8", …

Security ❀ TCP异常报文详解

文章目录 1. TCP Out-Of-Order2. TCP Previous Segment Lost3. TCP Retransmission4. TCP Dup Ack XXX#X5. TCP Windows Update6. TCP Previous segment not captured7. 异常案例分析 TCP协议中seq和ack seq的联系&#xff1a; id4的http请求报文由客户端发向服务器&#xff0…

sqli-labs-master靶场训练笔记(1-22|新手村)

2024.1.21 level-1 &#xff08;单引号装饰&#xff09; 先根据提示建立一个get请求 在尝试使用单个单引号测试&#xff0c;成功发现语句未闭合报错 然后反手一个 order by 得到数据库共3列&#xff0c;-- 后面加字母防止浏览器吃掉 -- 操作&#xff08;有些会&#xff09…

leetcode-hot100矩阵专题

73.矩阵置零 题目链接 73. 矩阵置零 - 力扣&#xff08;LeetCode&#xff09; 解题代码 class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""mapx []…

Reactor简述

1、概念 Reactor是一个计算机编程模式,它在并发编程和网络编程中用于处理大量并发输入事件。在该模型中,一个中心调度组件(即Reactor)负责监听和分发来自多个客户端的事件到相应的处理器或回调函数。当有事件发生时,如网络连接请求、数据到达等,Reactor能够及时响应并高效…

maven helper 解决jar包冲突方法

一 概要说明 1.1 说明 首先&#xff0c;解决idea中jar包冲突&#xff0c;使用maven的插件&#xff1a;maven helper插件&#xff0c;它能够给我们罗列出来同一个jar包的不同版本&#xff0c;以及他们的来源&#xff0c;但是对不同jar包中同名的类没有办法。 1.2 依赖顺序 …