内存池是什么原理?|内存池简易模拟实现|为学习高并发内存池tcmalloc做准备
前言
那么这里博主先安利一些干货满满的专栏了!
这两个都是博主在学习Linux操作系统过程中的记录,希望对大家的学习有帮助!
操作系统Operating Syshttps://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482
Linux Syshttps://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014.3001.5482这两个是博主学习数据结构的同时,手撕模拟STL标准模版库各种容器的专栏。
STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html
什么是内存池
因此最近博主准备开始学习Google的tcmalloc技术了,其实它就是一个高并发的内存池,效率做到极致,因此在此之前,博主先稍微学习了一下内存池的基本技术和基本概念,为后面的高并发内存池项目做准备,今天先给大家带来这个简单的内存池实现技术的简单demo代码。
所以本博客的代码,这是内存池的demo代码,实际上的内存池,比这个要复杂的多!这里的代码只是供学习使用。tcmalloc技术才是学习内存池的目标。
什么是内存池?
内存池是一种用来管理计算机程序中内存分配和释放的技术。它类似于一个预先准备好的内存存储区域,程序可以从中获取内存块而不是频繁地向操作系统请求。这种方式更高效,因为内存块的分配和释放开销较小,并且可以避免碎片化。内存池提高了程序的性能和响应速度,特别适用于频繁创建和销毁对象的场景,如网络服务器和并发编程。C语言的malloc,本质就是一个内存池。
内存池的基本原理
为了更高效的处理向内存获取资源,向内存获取资源,向内存释放资源,内存池一般这么做:
博主为了大家好理解,说的比较通俗:
- 内存池初始化的时候,直接向内存先要一大块资源。
- 向内存池获取资源的时候,从刚才初始化获取的一大块内存中切出一部分交给上层使用。
- 当进程向内存池释放资源的时候,不直接释放到系统中,而是首先把不用的空间,交给内存池,内存池内部维护一个单链表,把还回来的资源串起来。
- 当大块内存用完的时候,再向系统索要新内存。
- 当进程索取资源的时候,优先从链表中获取还回来的资源,如果链表中没有资源了,再向系统索要资源。
ObjectPool.hpp
#ifndef __OBJECT_POOL__
#define __OBJECT_POOL__
#include <iostream>
#include <vector>
// #include "../utils/Log.hpp" // 大家下载代码的时候可以暂时不要日志
#define __DEFAULT_KB__ 128
/*
其实这里直接使用malloc的,malloc其实自己就是C语言的一个内存池,
其实我们可以直接用系统调用,跳过malloc,这样测试现象更明显
调用SystemAlloc替换malloc即可
*/
inline static void *SystemAlloc(size_t kpage)
{
void *ptr = nullptr;
#ifdef _WIN32
*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;
#endif
}
template <class T>
class ObjectPool
{
private:
char *__memory = nullptr; // char 方便切
size_t __remain_bytes = 0; // 大块内存在切的过程中剩余的字节数
void *__free_list = nullptr; // 还回来的时候形成的自由链表
public:
T *New()
{
T *obj = nullptr;
// 不够空间 首选是把还回来的内存块对象进行再次利用
if (__free_list)
{
// 头删
void *next = *((void **)__free_list);
obj = (T *)__free_list;
__free_list = next;
return obj;
}
if (__remain_bytes < sizeof(T))
{
// 空间不够了,要重新开一个空间
__remain_bytes = __DEFAULT_KB__ * 1024;
__memory = (char *)malloc(__remain_bytes);
if (__memory == nullptr)
{
throw std::bad_alloc();
// logMessage(ERROR, "ObjectPool::New() malloc error");
}
}
obj = (T *)__memory;
size_t obj_size = sizeof(T) < sizeof(void *) ? sizeof(void *) : sizeof(T); // 小于一个指针的大小就给一个指针的大小就行
__memory += obj_size;
__remain_bytes -= obj_size;
// 定位new,显示调用T的构造函数,让T初始化一下
new (obj) T;
return obj;
}
void Delete(T *obj)
{
// 这样写无论是第一次插入还是后面的插入,都可以
// 这样写无论是32位还是64位,都可以
obj->~T(); // 显示调用析构函数
*(void **)obj = __free_list;
__free_list = obj;
}
};
#endif
testPerf.hpp
用于测试这个简易内存池的性能
#include "ObjectPool.hpp"
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 = 10000000;
size_t begin1 = clock();
std::vector<TreeNode *> v1;
v1.reserve(N);
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();
ObjectPool<TreeNode> TNPool;
size_t begin2 = clock();
std::vector<TreeNode *> v2;
v2.reserve(N);
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();
std::cout << "new cost time:" << end1 - begin1 << std::endl;
std::cout << "object pool cost time:" << end2 - begin2 << std::endl;
}
test.cc
#include "testPerf.hpp"
int main()
{
TestObjectPool();
return 0;
}
测试结果
内存池是什么原理?|内存池简易模拟实现|为学习高并发内存池tcmalloc做准备的更多相关文章
- Java高并发 -- 线程池
Java高并发 -- 线程池 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. ...
- 深入源码分析Java线程池的实现原理
程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对CPU利用的优化手段. 通过学习线程池原理,明白所 ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- Linux内存描述之高端内存--Linux内存管理(五)
1. 内核空间和用户空间 过去,CPU的地址总线只有32位, 32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间(232=4Gbit),在物理上理论上最多拥有4G内存(除了IO地址空间, ...
- Linux设备驱动程序学习之分配内存
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...
- Linux内存描述之高端内存–Linux内存管理(五)
服务器体系与共享存储器架构 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.7 X86 & arm gatieme LinuxDeviceDriver ...
- linux-3.2.36内核启动2-setup_arch中的内存初始化1(arm平台 分析高端内存和初始化memblock)【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/17093307 上一篇微博留下了这几个函数,现在我们来分析它们 sanity_c ...
- Java并发包源码学习系列:线程池ScheduledThreadPoolExecutor源码解析
目录 ScheduledThreadPoolExecutor概述 类图结构 ScheduledExecutorService ScheduledFutureTask FutureTask schedu ...
- Linux 内核高-低端内存设置代码跟踪(ARM构架)
对于ARM中内核如何在启动的时候设置高低端内存的分界线(也是逻辑地址与虚拟地址分界线(虚拟地址)减去那个固定的偏移),这里我稍微引导下(内核分析使用Linux-3.0): 首先定位设置内核虚拟地址起始 ...
- Linux内核高端内存 转
Linux内核地址映射模型x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通 ...
随机推荐
- AtCoder Regular Contest 121 (AB题解)
补题链接:Here A - 2nd Greatest Distance 二维坐标图中有 \(n\) 个房子,现在规定两房子距离定义为: \(max(|x_i−x_j|,|y_i−y_j|)\) 求第二 ...
- P2234
乐死我了,一道需要用平衡树的算法的题,在我忘了看标签的情况下下意识用了一个普及-难度的超简单思路解决了.当然其中加入了一些半骗分半贪心性质的剪枝. 总之这破算法竟然AC了就离谱,乐死我了 Code # ...
- P1825 诡异的题
一道不难的题,可是挑了好久也没调好!因为最开始写的代码太复杂了,一大堆嵌套括号,其中有一个ny写成了nx一直没发现.后来用新定义变量取代了那些复杂的用函数."."和方括号表达出来的 ...
- Winform的使用
Winform是什么 以下内容,来自朝夕教育课程,没有基础的可以去朝夕学学.这里主要是为了方便我自己回顾查询 创建Winform程序 Program类 Winform项目结构介绍 Winform控件简 ...
- RL 基础 | 如何注册自定义 gym 环境
如何 搭建 自定义 gym 环境:https://www.cnblogs.com/moonout/p/17174833.html 如何注册自定义 gym 环境: 博客:https://zhuanlan ...
- Redis 中bitMap使用及实现访问量
1. Bitmap 是什么 Bitmap(也称为位数组或者位向量等)是一种实现对位的操作的'数据结构',在数据结构加引号主要因为: Bitmap 本身不是一种数据结构,底层实际上是字符串,可以借助字符 ...
- SpringCloud集成Seata并使用Nacos做注册中心与配置中心
本文为博主原创,未经允许不得转载: 目录: 1. 下载并启动Seata Server,并指定nacos作为配置中心和注册中心 2. 同步seata server的配置到nacos 3. 启动Seata ...
- Angular系列教程之管道
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- PR 调整时间线宽度
1.问题 这里的宽度太小,不好进行下一步的调整 2.解决方法 方法一 按下=可以放宽 按下-(=左边的那个键)可以缩小宽度 方法二 拖动下方的滑动条即可 方法三 按住ALT+滚轮,即可调节
- [转帖]oracle ZHS16GBK的数据库导入到字符集为AL32UTF8的数据库(转载+自己经验总结)
字符集子集向其超集转换是可行的,如此例 ZHS16GBK转换为AL32UTF8. 导出使用的字符集将会记录在导出文件中,当文件导入时,将会检查导出时使用的字符集设置,如果这个字符集不同于导入客户端的N ...