【高并发内存池】定长内存池实现

目录

一、项目介绍

二、内存池介绍

2.1、池化技术

2.2、内存池

2.3、内存池主要解决的问题

三、malloc了解

四、设计定长内存池

五、和new(实际malloc)对比性能测试


一、项目介绍

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

malloc实际就是一个内存池,为什么还要实现tcmalloc,原因时tcmalloc是用于优化C++写的多线程应用,比glibc.23的malloc块,这个模块可以用来让MySQL在高并发内存占用更加稳定(多线程环境下比malloc快)。

优点是相比malloc在多线程管理下能做到更高效的内存管理,替代malloc和free。

二、内存池介绍

2.1、池化技术

"池化技术",就是程序先向系统申请过量资源,然后交由自己管理。之所以要申请过量资源,是因为每次申请内存的时候会有较大的开销,提前申请好内存,需要时使用,可以大大的提高程序运行效率。

在计算机中,除了内存池,还有连接池、线程池、对象池等都用到了池化这种技术。以服务器上的线程为例,它的主要思想是:启动诺干数量的线程,让他们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让它来处理客户端的请求,当处理完这个请求,线程又进入睡眠状态。

2.2、内存池

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

2.3、内存池主要解决的问题

1、效率问题

避免程序频繁向系统申请资源而产生较大的开销,自己提前申请足够内存供自己使用,可以提高程序运行效率。

2、内存碎片问题

在系统的内存分配器的角度,需要解决内存碎片问题,内存碎片有内碎片和外碎片两种。

内碎片问题是由于一些对齐需求,导致分配出去的空间中有一些内存无法被利用(部分小块空间利用不上)。

目前先介绍外碎片,所谓外碎片问题,是指一些空间的连续内存区域太小,这些内存空间不连续,以至于即使有足够的内存空间,也无法满足一些内存分配申请的需求(一段连续空间被切出去好多快,但是只有部分还回来了,但是它们不连续,导致要申请超过某个空间申请不出来,即使总空间大于要申请的空间,也申请不下来)。

三、malloc了解

C/动态申请内存都是通过malloc区堆上申请内存,C++借助new/delete去堆上申请释放内存空间,new和delete里面封装的是operator new和operator delete,operator new要符合C++抛异常机制里面封装了malloc,因此在C/C++中申请释放内存块,最终还是使用malloc和free的。

但是我们要知道,我们实际不是区堆上获取内存的。通过调用malloc函数,malloc调用brk()向操作系统申请内存,操作系统执行brk()系统调用,再将申请的内存通过malloc返回。

而malloc就是一个内存池。malloc相当于向操作系统申请了较大的内齿空间,然后零售给程序使用。当全部零售完或者程序需要大量的内存需求时,再根据实际需求向操作系统中申请"进货"内存。malloc的实现方式有多种方式,不同的编译器平台用的都是不同的。例如windows的vs系统用自己的微软写的一套,linux gcc用的glibc中的ptmalloc。

四、设计定长内存池

尺有所短,寸有所长,任何事物都有其优缺点,使用malloc申请内存是广泛应用于各种场景的,但是在什么场景下都可以使用,意味着什么场景下都不会有很高的性能,定长内存池是高并发内存池后续使用的基础组件,本先了解实现该内存池的实现

定长内存池可以满足固定大小的申请释放需求,特点是性能达到极致,不需要考虑内存碎片问题。

需要定义一个变量_memory指向申请的大块内存空间,当申请的固定大小内存用完返回后时候需要通过_freeList自由链表把还回来的内存块链接起来管理。

定长内存池可以通过malloc向系统申请空间,也可以直接向操作系统申请空间,window和Linux下可以直接向堆申请页为单位的大块内存。

windows用的接口是VirtuallAlloc,直接跳过malloc内存池,直接找堆按页为单位申请内存。

Linux通过调用brk()和nmap()向系统申请空间。

cLinux进程分配内存的两种方式--brk() 和mmap()

VirtualAlloc

#pragma once
#include<iostream>
#include<vector>
#include<time.h>
#include<stdlib.h>
using std::cout;
using std::endl;

#ifdef _WIN32
#include<windows.h>;
#else
#endif

//直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	//kpage左移13位 为 128k
	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;
}
template<class T>
class ObjectPool
{
public:
	T* New()
	{
		T* obj = nullptr;
		//_freeList不为空 优先把还回来的内存块对象重复利用
		if (_freeList)
		{
			//将自由链表头指针赋值给obj对象指针
			//
			obj = (T*)_freeList;
			_freeList = *(void**)obj;
		/*	void* next = *((void**)_freeList);
			obj =(T*)_freeList;
			_freeList = next;*/
		}
		else
		{
		//剩余内存不够一个对象大小时,则重新开辟空间
		//要被128*1024整除 刚好就是完整的对象
		//没有被整除 剩余的一小块不够一个对象来用
		//用_remainBytes<sizeof(T)来解决
			if (_remainBytes < sizeof(T))
			{
				_remainBytes = 128 * 1024;
				//_memory = (char*)malloc(_remainBytes);//128kb
				//128k右移13位 为 16页 
				_memory = (char*)SystemAlloc(_remainBytes>>13);

				if (_memory == nullptr)
				{
					printf("malloc fail!\n");
					exit(-1);
				}
			}
		//面临问题:
		//内存块可能用完 New申请时需要判断
			obj = (T*)_memory;
		//保证可以存下下一个对象的地址
			size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
			_memory += objSize;
			_remainBytes -= objSize;
			/*_memory += sizeof(T);
			_remainBytes -= sizeof(T);*/
		}
		//定位new,显示调用T的构造函数初始化
		//对已经有的空间进行初始化
		new(obj)T;
		return obj;
	}
	void Delete(T* obj)
	{
		//显示调用析构函数清理对象
		obj->~T();

		//obj的头四个字节存放自由链表的头指针存
		//更新头指针
		*(void**)obj = _freeList;
		_freeList = obj;
		//if (_freeList == NULL)
		//{
		//	_freeList = obj;
		//	*(void**)obj = nullptr;
		//}
		//else
		//{
		//	//头插
		//	*(void**)obj = _freeList;
		//	_freeList = obj;
		//}
	}
private:
	char* _memory=nullptr;//指向大块内存块的指针
	size_t _remainBytes = 0;//大块内存块在切分过程中剩余的字节数
	void* _freeList=nullptr;//还回来过程中链接的自由链表的头指针
};


五、和new(实际malloc)对比性能测试

#define _CRT_SECURE_NO_WARNINGS 1
#include"ObjectPool.h"

class TreeNode
{
public:
	TreeNode() :_val(0), _left(nullptr), _right(nullptr)
	{
	}
	int _val;
	TreeNode* _left;
	TreeNode* _right;
};
//测试定长内存池和new轮转释放内存效率
void PerformanceTest()
{
	//申请释放的轮次
	const size_t Rounds = 3;
	//每轮申请释放的次数
	const size_t N = 100000;
	size_t begin1 = clock();
	std::vector<TreeNode*> v1;
	v1.reserve(N);
	for (size_t i = 0; i < Rounds; i++)
	{
		for (size_t j = 0; j < N; j++)
		{
			v1.push_back(new TreeNode);
		}
		for (size_t j = 0; j < N; j++)
		{
			delete v1[j];
		}
		v1.clear();
	}
	size_t end1 = clock();

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

	cout << "new cost time:" << end1 - begin1 << endl;
	cout << "Object pool cost time:" << end2 - begin2 << endl;
}
int main()
{
    PerformanceTest();
	return 0;
}

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

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

相关文章

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

739.每日温度 496.下一个更大元素 I

739.每日温度 496.下一个更大元素 I 739.每日温度 力扣题目链接(opens new window) 请根据每日 气温 列表&#xff0c;重新生成一个列表。对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位…

MSVS C# Matlab的混合编程系列1 - 看似简单的问题引出

前言&#xff1a; 问题提出&#xff0c;如何把Matlab(本文简称MT)的算法集成到Visual Studio(本文简称VS)里面运行&#xff1f; 本文&#xff0c;通过编制一个MT中最简单的加法函数&#xff0c;我们把他做成 MSVS C#能够使用的动态库&#xff0c;说明了MSVS C# 和 MT集成的最…

Adobe全新AI驱动的Premiere Pro功能消除了枯燥的音频编辑任务

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

亚信安慧AntDB数据库:开辟数据库新纪元

在数字化时代的快速发展中&#xff0c;数据成为企业的核心资产&#xff0c;而数据库则是管理和利用这些数据的关键。 为满足企业日益复杂的混合负载场景和混合数据类型业务需求&#xff0c;亚信科技AntDB提出了全新的“超融合”理念。该理念将多引擎、多能力融合在一起&#x…

Docker(二)安装指南:主要介绍在 Linux 、Windows 10 和 macOS 上的安装

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 安装 Docker Docker 分为 stable test 和 nightly 三个更新频道。 官方网站上有各种环境下的 安装指南&#xff0c;这里主要介绍 Docker 在…

Java进阶-Tomcat发布JavaWeb项目

对于云服务器&#xff0c;程序员一般不会陌生&#xff0c;如果项目需要发布到现网&#xff0c;那么服务器是必不可缺的一项硬性条件&#xff0c;那么如何在云服务器上部署一个项目&#xff0c;需要做哪些配置准备&#xff0c;下面就由本文档为大家讲解&#xff0c;本篇以Tomcat…

windows vscode jsoncpp cmake c++ 构建项目

jsoncpp的编译和使用推荐文章&#xff1a;jsoncpp的编译和使用 | 爱编程的大丙 (subingwen.cn)https://www.subingwen.cn/cpp/jsoncpp/从这个链接下载jsoncpp-master&#xff1a;https://github.com/open-source-parsers/jsoncpp 可以把这个文件夹名字改成jsoncpp&#xff0c;…

街机模拟游戏逆向工程(HACKROM)教程:[10]68K汇编add指令

我们之前已经介绍了move指令&#xff0c;从本章开始&#xff0c;我们会一步步介绍更多的M68K指令。 简介&#xff1a; add :加法指令 该指令的作用是[源操作数]加[目的操作数]&#xff0c;结果传递至[目的操作数]&#xff0c;[源操作数]保持不变。 例子&#xff1a;…

MySQL复合查询 内外连接

目录 前言&#xff1a; 多表查询&#xff1a; 显示部门号为10的部门名&#xff0c;员工名和工资 : 显示各个员工的姓名&#xff0c;工资&#xff0c;及工资级别: 自连接 显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号&#xff09; 子查询 单行子查询&#…

汽车微电机行业研究:预计2029年将达到188亿美元

微电机行业是技术密集型行业&#xff0c;其起源于欧洲的德国、瑞士等国家&#xff0c;发展于日本。随着改革开放&#xff0c;中国作为发展中国家&#xff0c;承接了德国、日本等发达国家的汽车微电机产业转移&#xff0c;技术扩散逐步向我国转移。 微特电机广泛应用于信息处理设…

[Python] 如何通过ctypes库来调用C++ 动态库 DLL?

ctypes库介绍 ctypes是Python的一个外部库,它提供了一种灵活的方式来调用C语言的动态链接库(DLL)或共享库(SO)。通过ctypes,我们可以在Python中直接调用C语言编写的函数和变量,从而实现跨语言的互操作。 ctypes 它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的…

数学建模美赛资料(赛题+获奖论文更新)

数学建模美赛历年真题可以帮助我们了解比赛的出题思路&#xff0c;对建模比赛有一个大致的了解。 在备赛过程中&#xff0c;通过往年真题&#xff0c;我们可以了解考试的范围和重点&#xff0c;做到心中有数&#xff0c;可以有的放矢。通过真题&#xff0c;我们可以感受到各个…

Java 方法(方法的调用机制、方法的传参机制)

方法 方法就是将一个功能抽取出来&#xff0c;把代码单独定义在一个大括号内&#xff0c;形成一个单独的功能。 当需要这个功能的时候&#xff0c;就可以去调用。这样即实现了代码的复用性&#xff0c;也解决了代码冗余的现象。 修饰符 返回值类型 方法名 &#xff08;参数列…

CC工具箱使用指南:【计算面积】

一、简介 在Arcgis中&#xff0c;如果要计算面要素的面积&#xff0c;有几种方法。 1、gdb数据会自带一个shape_area字段&#xff0c;这就是面的平面面积&#xff0c;单位是平方米&#xff1a; 2、在双精度字段上右键单击&#xff0c;在弹出的菜单中点击【计算几何】&#xf…

制造业企业数字化转型难点剖析及解决之法

导语 全球正在由工业经济向数字经济转型过渡&#xff0c;制造业正在且并将长期处于数字化转型发展阶段&#xff0c;并沿着数字化、网络化、智能化阶段不断跃升。但如何找准数字化转型的切入点&#xff0c;以低耗能、低成本、高效率的方式加快制造业转型升级的步伐&#xff0c;仍…

普兰资产(PLAN B KRYPTO ASSETS):Schutz AI 公链引领数字资产新时代

比特B ETF是金融技术革命的起始 普兰资产&#xff08;PLAN B KRYPTO ASSETS&#xff09;执行长Jonah Fischer指出&#xff0c;比特B ETF 仅是迈向金融领域技术革命的首个阶段。他认为比特B现货 ETF 提供了投资者接触年轻且具有风险性的资产的途径&#xff0c;但他强调区块链技术…

Linux自动化构建工具——make和Makefile使用详解

一、初步认识make和Makefile 我们首先需要知道的是&#xff0c;make是一个命令&#xff0c;Makefile是一个文件&#xff0c;Makefile中包含了依赖关系和依赖方法。 从上面的文件以及指令中我们可以看到&#xff0c;我们可以在Makefile文件中写入依赖关系以及对应的依赖方法&…

2024执业医师考试报名流程及上传照片要求详解

2024年执业医师和助理医师考试的报名工作将于1月22日正式启动&#xff0c;报名截止日期为2月4日。建议考生尽早报名&#xff0c;以避免在报名截止日期临近时出现拥挤情况。您可根据本文介绍&#xff0c;提前准备好报名所需资料、证件照电子版和相关证明材料&#xff0c;并了解报…

【我与Java的成长记】之多态,重载与重写详解

系列文章目录 能看懂文字就能明白系列 C语言笔记传送门 Java笔记传送门 &#x1f31f; 个人主页&#xff1a;古德猫宁- &#x1f308; 信念如阳光&#xff0c;照亮前行的每一步 文章目录 系列文章目录&#x1f308; *信念如阳光&#xff0c;照亮前行的每一步* 前言一、多态的概…
最新文章