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

目录

一、项目介绍

二、内存池介绍

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位 为 128kvoid* 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;//_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.hqwc.cn/news/412796.html

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

相关文章

Docker-02-镜像项目部署

Docker-02-镜像&项目部署 文章目录 Docker-02-镜像&项目部署一、镜像①&#xff1a;镜像结构②&#xff1a;Dockerfile③&#xff1a;构建镜像01&#xff1a;构建02&#xff1a;查看镜像列表03&#xff1a;运行镜像 二、网络①&#xff1a;容器的网络IP地址②&#xff…

SQL性能分析

SQL性能分析 1、SQL执行频率 ​ MySQL 客户端连接成功后&#xff0c;通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令&#xff0c;可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次&#xff1a; -- session 是查看当前会话 ; …

oracle11g的闪回技术-闪回表-时间戳

--数据库闪回表 --1创建表&#xff08;登录模式system&#xff09; CREATE table dept2 as select * from dept;--此语句如果加上where条件可用于工作中数据的临时备份 select * from dept2;--查询新建表信息 --进入sql>set time on 通过时间点闪回 记录弹出的时间点&#…

flutter 五点一:MaterialApp Theme

ThemeData factory ThemeData({bool? applyElevationOverlayColor, //material2的darkTheme下 增加一个半透明遮罩 来凸显阴影效果 material3下无效 貌似没啥用NoDefaultCupertinoThemeData? cupertinoOverrideTheme, //ios组件样式 Iterable<ThemeExtension<dyn…

zabbix客户端配置及自定义监控

部署zabbix客户机 1.服务端和客户端都配置时间同步 yum install -y ntpdate ntpdate -u ntp.aliyun.com 2.服务端和客户端都设置 hosts 解析 cat > /etc/hosts << EOF 172.16.23.16 localhost 172.16.23.17 zbx-server EOF 3.被监控端 //设置 zabbix 的下载源&…

MySQL深度分页优化问题

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

广告投放场景中ABtest分析的评价、优化和决策建议

目录 写在开头1. AB测试基础知识1.1 AB测试概述1.2 原理和流程1.3 广告领域中的AB测试应用 2. 评价广告投放中的AB测试2.1 关键指标选择与解释2.2 统计学方法应用 3. AB测试分析中的常见问题与解决方案3.1 样本偏差3.2 季节性影响3.3 测试时长选择3.4 结果误解与分析失误 4. 优…

老师布置作业的技巧有哪些

布置作业可不只是简单地给学生分配任务&#xff0c;而是需要运用一些技巧&#xff0c;以达到更好的教学效果。那么&#xff0c;老师应该如何布置作业呢&#xff1f; 一、作业要有针对性 布置作业时&#xff0c;老师应该根据学生的实际情况和课程要求&#xff0c;有针对性地设…

tinyxml2

使用tinyxml2&#xff0c;得知道一些xml基础 xml tutorial--菜鸟 tinyxml2类对象 链接 结构 XMLNode 什么是节点 节点&#xff1a;元素、声明、文本、注释等。 XMLDocument xml文档(文件)对象。 作用&#xff1a; 加载xml文件&#xff0c; tinyxml2作用 先定义两个宏 …

阿里云服务器地域如何选择?哪个地域价格优惠一些?

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…

【数据结构】堆的实现和排序

目录 1、堆的概念和结构 1.1、堆的概念 1.2、堆的性质 1.3、堆的逻辑结构和存储结构 2、堆的实现 2.1、堆的初始化和初始化 2.2、堆的插入和向上调整算法 2.3、堆的删除和向下调整算法 2.4、取堆顶的数据和数据个数 2.5、堆的判空和打印 2.6、测试 3、堆的应用 3.1…

CentOS查看修改时间

经常玩docker的朋友应该都知道&#xff0c;有很多的镜像运行起来后&#xff0c;发现容器里的系统时间不对&#xff0c;一般是晚被北京时间8个小时&#xff08;不一定&#xff09;。 这里合理怀疑是镜像给的初始时区是世界标准时间&#xff08;也叫协调世界时间&#xff09;。 有…