定长内存池的实现

 


文章目录

  • 什么是内存池
    • 池化技术
    • 内存池
    • 内存池主要解决的问题
    • malloc
  • 定长内存池的实现

 前言

        当前项目是实现一个高并发的内存池,他的原型是Google 的一个开源项目 tcmalloc tcmalloc 全称 Thread-Caching Malloc ,即线程缓存的 malloc ,实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数( malloc free )。

一、什么是内存池

Google开源tcmalloc源码

1.池化技术

池化技术是将程序中需要经常使用的核心资源先申请出 来,放到一个池,由程序源自己管理,这样可以提高资源的使用效率,它可以避免核心资源申请和释放带来的开销,也可以保证本程序占有的资源数量。 经常使用的池化技术包括内存池、线程池和连接池等。

2.内存池

内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操 作系统,而是返回内存池。当程序退出 ( 或者特定时间 ) 时,内存池才将之前申请的内存真正释放。

3.内存池主要解决的问题

内存池主要解决的当然是效率的问题,其次如果作为系统的内存分配器的角度,还需要解决一下内存碎片的问题。那么什么是内存碎片呢?
再需要补充说明的是内存碎片分为外碎片和内碎片,上面我们讲的外碎片问题。外部碎片是一些空闲的连续内存区域太小,这些内存空间不连续,以至于合计的内存足够,但是不能满足一些的内存分配申请 需求。内部碎片是由于一些对齐的需求,导致分配出去的空间中一些内存无法被利用。

4.malloc

C/C++ 中我们要动态申请内存都是通过 malloc 去申请内存,但是我们要知道,实际我们不是直接去堆获取内存的, malloc 就是一个内存池。 malloc() 相当于向操作系统 批发 了一块较大的内存空间,然后 零售 给程 序用。当全部 售完 或程序有大量的内存需求时,再根据实际需求向操作系统 进货 malloc 的实现方 式有很多种,一般不同编译器平台用的都是不同的。比如 windows vs 系列用的微软自己写的一套, linux gcc 用的 glibc 中的 ptmalloc。
malloc背后的实现原理---内存池
malloc底层实现
一文了解,Linux内存管理,malloc、free 实现原理

 

二、定长内存池的实现

 

 

ObjectPool.h

#include <iostream>
#include <time.h>
#include <vector>

using std::cout;
using std::endl;
using std::bad_alloc;

//利用创建二叉树的节点数来衡量效率的方式
struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;

	TreeNode() :val(0), left(nullptr), right(nullptr)
	{}
};

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);
				if (_memory == nullptr)
					throw 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;//定义自由链表
};

void TestObjectPool()
{
	// 申请释放的轮次
	const size_t Rounds = 5;

	// 每轮申请释放多少次
	const size_t N = 100000;

	std::vector<TreeNode*> v1;
	v1.reserve(N);

	size_t begin1 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (int i = 0; i < N; ++i)
		{
			v1.push_back(new TreeNode);
		}
		for (int i = 0; i < N; ++i)
		{
			delete v1[i];
		}
		v1.clear();
	}

	size_t end1 = clock();

	std::vector<TreeNode*> v2;
	v2.reserve(N);

	ObjectPool<TreeNode> TNPool;
	size_t begin2 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (int i = 0; i < N; ++i)
		{
			v2.push_back(TNPool.New());
		}
		for (int i = 0; i < N; ++i)
		{
			TNPool.Delete(v2[i]);
		}
		v2.clear();
	}
	size_t end2 = clock();

	cout << "new cost time:" << end1 - begin1 << endl;
	cout << "object pool cost time:" << end2 - begin2 << endl;
}

 Test.cpp

#include "ObjectPool.h"

int main()
{
	TestObjectPool();
	return 0;
}

//直接去堆上按页申请空间

 windowsLinux下如何直接向堆申请页为单位的大块内存:

VirtualAlloc

brk和mmap

ObjectPool.h

#include <iostream>
#include <time.h>
#include <vector>

using std::cout;
using std::endl;
using std::bad_alloc;

#ifdef _WIN32
	#include<Windows.h>
#else
	
#endif


inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage * (1 << 12), MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE);
#else
	// linux下brk mmap等
#endif
	if (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);
				if (_memory == nullptr)
					throw 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;//定义自由链表
};

//利用创建二叉树的节点数来衡量效率的方式
struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;

	TreeNode() :val(0), left(nullptr), right(nullptr)
	{}
};

void TestObjectPool()
{
	// 申请释放的轮次
	const size_t Rounds = 5;

	// 每轮申请释放多少次
	const size_t N = 100000;

	std::vector<TreeNode*> v1;
	v1.reserve(N);

	size_t begin1 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (int i = 0; i < N; ++i)
		{
			v1.push_back(new TreeNode);
		}
		for (int i = 0; i < N; ++i)
		{
			delete v1[i];
		}
		v1.clear();
	}

	size_t end1 = clock();

	std::vector<TreeNode*> v2;
	v2.reserve(N);

	ObjectPool<TreeNode> TNPool;
	size_t begin2 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (int i = 0; i < N; ++i)
		{
			v2.push_back(TNPool.New());
		}
		for (int i = 0; i < N; ++i)
		{
			TNPool.Delete(v2[i]);
		}
		v2.clear();
	}
	size_t end2 = clock();

	cout << "new cost time:" << end1 - begin1 << endl;
	cout << "object pool cost time:" << end2 - begin2 << endl;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python用户价值分析

数据获取&#xff1a; 表格数据 数据清洗后数据&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1D7qOZqKmF3YR3meQPsp3sQ 提取码&#xff1a;1234 数据下载下来后&#xff0c;先进行数据清洗。数据清洗在进行用户价值分析,也可以直接下载我清洗后的数据。 RFM模型&a…

springcloud微服务架构搭建过程

项目地址&#xff1a;源代码 仅作为学习用例使用&#xff0c;是我开发过程中的总结、实际的一部分使用方式 开发环境&#xff1a; jdk11 springboot2.7.6 springcloud2021.0.5 alibabacloud 2021.0.4.0 redis6.0 mysql8.0 一、项目搭建 wdz-api&#xff1a;存放远程服务调用相关…

如何选电脑

1、CPU&#xff08;中央处理器&#xff09; 怎么看CPU型号&#xff1a;CPU:系列-代数等级核心显卡型号电压后缀 例如CPU:i7-10750H &#xff1a; 1、系列&#xff1a;Intel的酷睿i3、i5、i7、i9这四个系列的CPU&#xff0c;数字越大就代表越高端。 2、代数&#xff1a;代表…

自主HttpServer实现(C++实战项目)

文章目录项目介绍CGI技术概念原理设计框架日志文件TCPServer任务类初始化与启动HttpServerHTTP请求结构HTTP响应结构线程回调EndPoint类EndPoint主体框架读取HTTP请求处理HTTP请求CGI处理非CGI处理构建HTTP响应发送HTTP响应接入线程池简单测试项目扩展项目介绍 该项目是一个基…

大厂光环下的功能测试,出去面试自动化一问三不知

在一家公司待久了技术能力反而变弱了&#xff0c;原来的许多知识都会慢慢遗忘&#xff0c;这种情况并不少见。 一个京东员工发帖吐槽&#xff1a;感觉在大厂快待废了&#xff0c;出去面试问自己接口环境搭建、pytest测试框架&#xff0c;自己做点工太久都忘记了。平时用的时候…

无公网IP,SSH远程连接Linux CentOS服务器【内网穿透】

文章目录视频教程1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程本次教程我们来实现如何在外公网环境下&#xff0c;SSH远程连接家里/公司的Linux CentOS服务器&#xff0c;无需公网IP&#xff0c;也不需要设置路…

地表最强,接口调试神器Postman ,写得太好了

postman是一款支持http协议的接口调试与测试工具&#xff0c;其主要特点就是功能强大&#xff0c;使用简单且易用性好 。 无论是开发人员进行接口调试&#xff0c;还是测试人员做接口测试&#xff0c;postman都是我们的首选工具之一 。 那么接下来就介绍下postman到底有哪些功…

吉林省互联网医院资质申请条件|牌照申请

吉林省互联网医院资质申请条件|牌照申请|长春市|四平市|辽源市|通化市|白山市|松原市|白城市|延边朝鲜族自治州 吉林省互联网医院资质申请条件   一、《医疗机构管理条例》第十六条申请医疗机构执业登记&#xff0c;应具备下列条件&#xff1a;   1.有设置医疗机构批准书&a…

论文笔记 | 标准误聚类问题

关于标准误的选择&#xff0c;如是否选择稳健性标准误、是否采取聚类标准误。之前一直是困惑的&#xff0c;惯用的做法是类似主题的文献做法。所以这一次&#xff0c;借计量经济学课程之故&#xff0c;较深入学习了标准误的选择问题。 在开始之前推荐一个知乎博主。他阅读了很…

【vue2】axios请求与axios拦截器的使用详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;当我们在路由跳转前与后我们可实现触发的操作 【前言】ajax是一种在javaScript代码中发请…

使用对象存储库管理 UFT 中的对象

1. 记录一个新订单 在UFT 菜单栏中&#xff0c;选择 File] New|Test 创建一个新的测试。单击 Record&#xff0c;出现 Record and Run Settings。单击 Record and Run Settings 对话框的OK 按钮。单击 New Order 按钮&#xff0c;设置初始条件。输入以下航班信息。航班日期: 选…

台灯有必要买一百多的吗?2023专家建议孩子买台灯

问题&#xff1a;台灯有必要买一百多的吗&#xff1f; 回答&#xff1a;不建议买一百多的台灯&#xff0c;建议选择国AA级的台灯 现在许多学生出现视力问题&#xff0c;原因是在平时没有注意到不良好的用眼环境 孩子早早戴上小眼镜&#xff0c;家长不惜花心思去买各种视力保护…

flstudio怎么改主题,如何更改FL Studio21背景图片

fl studio作为一款功能强大且实用的音频处理和音乐制作软件&#xff0c;其精致的界面布局一直为众多音乐人所喜爱&#xff0c;但是fl studio编曲软件安装后初始内置的灰黑色工作区背景&#xff0c;难免成为美中不足的一点。 那么用户如何根据自己的喜好设置工作区背景呢&#x…

Java基础知识

Java基础知识 一、计算机开发语言发展 计算机语言总体可分为机器语言、汇编语言、高级语言三大类&#xff0c;这三类开发语言恰恰是计算与开发语言的三个阶段。 机器语言&#xff1a;机器语言是第一代计算机开发语言&#xff0c;是通过最原始的穿孔卡片&#xff08;二进制有孔…

《疯狂Java讲义》读书笔记3

这两天总结了数据结构中栈的用法&#xff0c;对栈的初始化、出栈、入栈的总结&#xff1a; http://t.csdn.cn/7sKjQ 对双栈共享的初始化、入栈、出栈的总结&#xff1a; http://t.csdn.cn/4WXCO 调用父类构造器 子类不会获得父类的构造器&#xff0c;但是可以调用父类构造…

聊一聊前端的性能指标

一、前端性能指标有哪些&#xff1f; 根据 chrome Lighthouse 最新规则&#xff0c;前端性能指标考量主要有 FCP&#xff08;First Contenful Paint&#xff09;、SI&#xff08;Speed Index&#xff09;、LCP&#xff08;Largest Contentful Paint&#xff09;、TBT&#xff…

四个常见的Linux技术面问题

刚毕业要找工作了&#xff0c;只要是你找工作就会有面试这个环节&#xff0c;那么在面试环节中&#xff0c;有哪些注意事项值得我的关注呢&#xff1f;特别是专业技术岗位&#xff0c;这样的岗位询问一般都是在职的工程师&#xff0c;如何在面试环节更好地理解面试官的问题&…

IDEA的热部署【MyBatis XML热部署 】

本文适用修改JAVA代码热部署、MyBatis XML的热部署。 一、JAVA代码热部署. 新版IDEA中&#xff1a;开启允许在运行过程中修改文件 最后要在Debug模式启动,可以看到热部署的加载文件了&#xff0c;可以手动点左边那个图标立即加载生效. 二、MyBatis XML修改热部署. MybatisMap…

MFCC语音特征值提取算法

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

银行数字化转型导师坚鹏:银行产品经理技能快速提升之道

银行产品经理技能快速提升之道 ——以推动银行战略目标实现为核心&#xff0c;实现知行果合一课程背景&#xff1a; 很多银行都在开展产品经理技能提升工作&#xff0c;目前存在以下问题急需解决&#xff1a; 不知道银行产品经理掌握哪些关键知识&#xff1f; 不清楚如何有效…