【项目日记(二)】开胃菜--定长池的实现

这篇具有很好参考价值的文章主要介绍了【项目日记(二)】开胃菜--定长池的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:项目日记-高并发内存池⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝
开发环境: Visual Studio 2022


【项目日记(二)】开胃菜--定长池的实现,项目日记--高并发内存池,内存池,c++,项目日记


1. 前言

在真正开始实现并发内存池前
我们先做一个子项目-定长池.
掌握了定长池对后面学习并发内存池
有很大的帮助,并且定长池本身也是
并发内存池的一个组件!

本章重点:

本篇文章着重讲解定长池的模拟实现
,其中涉及到自由链表的链接问题,如何
彻底放弃malloc的问题以及如何处理
32位和64位下指针大小的区分的问题


2. 前期基础知识铺垫

我们还需要补充两个知识的内容:

  1. 如何彻底舍弃malloc?
  2. 如何将free掉的资源再次利用?

windows/Linux下如何直接申请内存:

Windows下的申请方式

Linux下的申请方式

由于博主使用的开发环境是Windows
所以我这里直接使用此函数了:

inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
	// linux下使用brk mmap等
#endif
	if (ptr == nullptr)
		throw std::bad_alloc();
	return ptr;
}

如何重复利用已经释放掉的内存:

采用自由链表的方式将所有释放
掉的空间全部连接在一起

【项目日记(二)】开胃菜--定长池的实现,项目日记--高并发内存池,内存池,c++,项目日记

有一个问题:既然自由链表中链接的是
已经释放掉的内存,那么就代表这块内
存已经没用了,所以在32/64位机器下,
我们直接让这份空间的头4/8个字节指
向下一份释放的资源,就不用定义next
指针了,并且,当申请空间的大小小于
4/8时我们将它扩成4/8来存储后面的地址


3. 定长池基础框架

//定长内存池
template<class T>
class ObjectPool
{
public:
	inline static void* SystemAlloc(size_t kpage)//向系统申请内存的函数
	{
#ifdef _WIN32
		void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
		// linux下brk mmap等
#endif

		if (ptr == nullptr)
			throw std::bad_alloc();
		return ptr;
	}
	T* New()
	{}

	void Delete(T* obj)
	{}

private:
	char* _memory = nullptr;     //向系统申请的一大块内存,申请了多少字节就从起始地址++多少次,所以定义为char*
	void* _freeList = nullptr;   //还回来的空间的过程中,链接的自由链表的头指针
	size_t _surplusBytes = 0;    //定长池剩余的空间大小(字节为单位)
};

关于代码的解释都在注释中,如果对架构
有不明白的地方欢迎私信.所以我们主要
是要实现两个函数,一个是new申请空间
一个是delete释放空间,并且需要注意的是,
这个定长池是一个类,在使用它时,同一类
型的变量申请空间会去同一个对象中申请


4. 定长池New的具体实现

在实现new函数之前我们要先想到
三个点:

  1. 当自由链表中有空间时,应该优先
    去自由链表中拿,而不是给它切分内存

  2. 当定长池剩余的空间不足时,需要扩容

  3. 若对象的大小小于4/8个字节,那么它
    就无法存储下一个位置的地址,此时需
    要将它直接扩为4/8字节来使用

T* New()
{
	T* obj = nullptr;
	//若有还回来的内存,优先使用它而不是去切割大内存
	if (_freeList != nullptr)
	{
		void* next = *(void**)_freeList;//先把头内存的后面一个内存找到,再把头内存头删了给外界用
		obj = (T*)_freeList;
		_freeList = next;
	}
	else
	{
		if (_surplusBytes < sizeof(T))//当剩余的空间不足以分配给T类型时,也要扩容
		{
			//_memory = malloc(128 * 1024);//128kbKB
			_surplusBytes = 128 * 1024;
			_memory = (char*)SystemAlloc(_surplusBytes);
			if (_memory == nullptr)
				throw std::bad_alloc();
		}
		obj = (T*)_memory;
		//若此对象的大小小于的指针的大小(4/8),则它释放后无法存储下一个内存的地址,要做特殊处理
		size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
		_memory += objSize;
		_surplusBytes -= objSize;
	}
	//定位new,显示调用T的构造函数初始化
	new(obj)T;
	return obj;
}

5. 对代码的解释

对代码的解释:

  1. 自由链表部分

void* next = * (int**)_freeList;这段代码的意思就是将自由链表的头节点的下一个节点提取出来,由于使用的机器是32还是64位是不确定的,所以将freelist强转为int**后,再解引用就是int* 类型的变量,假如你是32位那么int* 就占四位,假如你是64位那么int* 就占八位,完全省略了去判断电脑是多少位的必要.并且将自由链表中的数据返还给外界使用是用的头删的方法,因为效率更快!

【项目日记(二)】开胃菜--定长池的实现,项目日记--高并发内存池,内存池,c++,项目日记

  1. 直接切分内存部分

假如在64位机器下,一个指针的大小是8字节,但是用户申请了4字节的空间,这时我们需要将空间扩为八字节再返回给用户,因为一旦这份空间被返回,四字节是无法当成指针使用存储下一个节点地址的!并且切分内存后,_memory要向后移动,避免将同一份内存切分给不同的变量.在申请完内存后,需要使用定位new来对已经开辟好的空间做初始化.

【项目日记(二)】开胃菜--定长池的实现,项目日记--高并发内存池,内存池,c++,项目日记

  1. 定长内存池的物理结剖析

将已经释放的空间挂在自由链表上是逻辑的结构,但是实际的物理结构是不管自由链表悬挂了多少个内存块,它们实际上都是连在一起的在定长池中的,只不过已经被使用了的空间在指针_memory前面,而未分配内存的空间在_memory后面!

【项目日记(二)】开胃菜--定长池的实现,项目日记--高并发内存池,内存池,c++,项目日记


6. 定长池delete的具体实现

定长池的删除比较简单,不需要将空间
free掉,只需要将空间挂到自由链表上即可
并且使用头插的方式

具体代码如下:

void Delete(T* obj)
{
	//显示调用析构函数
	obj->~T();
	//使用**强转,不管是32位还是64位都没问题
	//取obj对象头四个字节来存储nullptr或下一个被Delete的对象的空间的地址
	//使用头插,不用每次都去找尾
	*(void**)obj = _freeList;
	_freeList = obj;
}

7. 总结以及小tips

定长池的实现是在为后面的并发内存
池打基础,请同学们耐心掌握这篇文章
的所有内容,后面会有大用处!

小tips:

我们在向系统申请内存时,一次性申请了256K也就是256*1024个字节的空间,也就是说我们的定长池最多只能256K文章来源地址https://www.toymoban.com/news/detail-759461.html


🔎 下期预告:高并发内存池总体框架🔍

到了这里,关于【项目日记(二)】开胃菜--定长池的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++项目】高并发内存池第五讲内存回收释放过程介绍

    项目源代码:高并发内存池 当闲置的内存超过一个批量单位大小的时候就开始回收,首先要计算出要回收到哪个桶的的内存,然后逐级往上回收。 CentralCache回收回来还需要做前后页的合并,合成一个大的内存块,然后继续交给PageCache处理 PageCache需要将一页一一页的小块内存

    2024年02月08日
    浏览(49)
  • 【项目设计】高并发内存池—tcmalloc核心框架学习

    目录 一、项目介绍 二、内存池的初步认识 2.1 池化技术 2.2 内存池 2.3 malloc 三、定长内存池 四、整体框架设计介绍 五、申请内存 5.1 ThreadCache 5.1.1 ThreadCache整体设计 5.1.2 ThreadCache哈希桶映射与对齐规则 5.1.3 TSL无锁访问 5.1.4 ThreadCache核心设计 5.2 CentralCache 5.2.1 CentralCache整体设

    2023年04月09日
    浏览(40)
  • 【项目设计】高并发内存池(Concurrent Memory Pool)

    目录 1️⃣项目介绍 🍙项目概述 🍙知识储备 2️⃣内存池介绍 🍙池化技术 🍙内存池 🍙内存池主要解决的问题 🍥内碎片 🍥外碎片 🍙malloc 3️⃣ 定长内存池设计 4️⃣ 项目整体框架实现 5️⃣Thread Cache设计 🍙自由链表 🍙对齐映射规则设计 🍥对齐大小计算 🍥映射桶号

    2024年02月10日
    浏览(37)
  • 关于一个C++项目:高并发内存池的开发过程(一)

    原项目地址: 高并发内存池项目: 高并发内存池项目的课堂板书+代码 (gitee.com) 本打算利用五一假期的时间将这个项目一口气开发完成,但由于本人的懈怠,这个项目最终只完成了80%。于是利用长假后的一天假期,将这个项目的框架搭建完成。本以为这个项目就此结束,但是

    2024年02月05日
    浏览(37)
  • 关于一个C++项目:高并发内存池的开发过程(二)

    上篇文章梳理了内存申请操作的流程,大概测试了一下,没有发现什么问题。这篇文章将梳理内存释放操作的流程,若申请操作中,有些细节没有把控好,那么释放操作将bug不断。有些bug我至今还在调试…所以,这篇文章的梳理,侧重点依然是逻辑结构。代码的细节可能存在

    2024年02月05日
    浏览(64)
  • 实现高并发内存池(C++)

    所谓“池化技术”,就是 程序先向系统申请过量的资源,然后自己管理 以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较大的开销,不如提前申请好,这样使用时就会变得非常快捷,大大提高程序运行效率。在计算机中,有很多使用“池”这种技术的

    2024年02月07日
    浏览(36)
  • 十一、做高并发内存池项目过程中遇到的bug以及调试bug的方法和心得

    第一个bug是内存问题,程序直接崩溃,问题出现在:GetOneSpan函数中的切分span的时候结尾的span1的next没有置空。 第二个bug是还小内存块给span的时候找不到小内存所属的span,原因是NewSpan函数中的一个if分支返回前没有把id和span*的映射关系存好。 修bug前: 修bug后: 第三个bug出

    2024年02月10日
    浏览(40)
  • 【项目日记(四)】第一层: 线程缓存的具体实现

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:项目日记-高并发内存池⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你做项目   🔝🔝 开发环境: Visual Studio 2022 由于此项目需要创建多个文件 所以我直接在.h文件中既放声明 也存放实现,减少文件的数量 本章重点: 本篇

    2024年01月25日
    浏览(21)
  • 【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩

    文章目录 背景介绍 初始代码 优化代码 分析和应用 总结         在一个嵌入式软件开发项目中,有一个使用 MATLAB Function 编写的算法模块,功能是从一个较大的数组中提取一段数据,然后求均值输出,示例如下:         一开始算法开发的思路非常简单,按照功能需

    2024年04月17日
    浏览(37)
  • Redis的内存淘汰策略有哪些?Redis的发布订阅功能是如何实现的?如何监控Redis的性能?Redis的并发竞争问题如何解决?

    Redis的内存淘汰策略有以下几种: noeviction :不进行任何内存淘汰,当内存用完时,新的写操作将会返回错误。 volatile-lru :在所有已设置过期时间的键中,使用近似LRU算法删除最长时间未使用的键,直到腾出足够的内存空间为止。 volatile-ttl :在所有已设置过期时间的键中,

    2024年02月12日
    浏览(80)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包