前言

那么这里博主先安利一些干货满满的专栏了!

这两个都是博主在学习Linux操作系统过程中的记录,希望对大家的学习有帮助!

操作系统Operating Syshttps://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482Linux 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做准备的更多相关文章

  1. Java高并发 -- 线程池

    Java高并发 -- 线程池 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. ...

  2. 深入源码分析Java线程池的实现原理

    程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对CPU利用的优化手段. 通过学习线程池原理,明白所 ...

  3. jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)

    jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...

  4. Linux内存描述之高端内存--Linux内存管理(五)

    1. 内核空间和用户空间 过去,CPU的地址总线只有32位, 32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间(232=4Gbit),在物理上理论上最多拥有4G内存(除了IO地址空间, ...

  5. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  6. Linux内存描述之高端内存–Linux内存管理(五)

    服务器体系与共享存储器架构 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.7 X86 & arm gatieme LinuxDeviceDriver ...

  7. linux-3.2.36内核启动2-setup_arch中的内存初始化1(arm平台 分析高端内存和初始化memblock)【转】

    转自:http://blog.csdn.net/tommy_wxie/article/details/17093307 上一篇微博留下了这几个函数,现在我们来分析它们         sanity_c ...

  8. Java并发包源码学习系列:线程池ScheduledThreadPoolExecutor源码解析

    目录 ScheduledThreadPoolExecutor概述 类图结构 ScheduledExecutorService ScheduledFutureTask FutureTask schedu ...

  9. Linux 内核高-低端内存设置代码跟踪(ARM构架)

    对于ARM中内核如何在启动的时候设置高低端内存的分界线(也是逻辑地址与虚拟地址分界线(虚拟地址)减去那个固定的偏移),这里我稍微引导下(内核分析使用Linux-3.0): 首先定位设置内核虚拟地址起始 ...

  10. Linux内核高端内存 转

        Linux内核地址映射模型x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图.   Linux内核地址空间划分 通 ...

随机推荐

  1. JSP开发模式(四种模式)

    原作者为 RioTian@cnblogs, 本作品采用 CC 4.0 BY 进行许可,转载请注明出处. 学习编程开发少不了学习开发模式, JSP在创立至今有 \(4\) 种流行的开发模式: 包括 JS ...

  2. Codeforces Round #717 (Div. 2) 个人题解 A~C (A思維,B位運算,C背包DP)

    1516A. Tit for Tat 題意: 給定大小為 \(n\) 的數組和可操作次數 \(k\) , 每次操作都選定兩個數(如果 \(1 \le a_i\) ),使第一個數 - \(1\) ,另一 ...

  3. L1-018 大笨钟 (10分)

    开始天梯赛专项训练 微博上有个自称"大笨钟V"的家伙,每天敲钟催促码农们爱惜身体早点睡觉.不过由于笨钟自己作息也不是很规律,所以敲钟并不定时.一般敲钟的点数是根据敲钟时间而定的,如 ...

  4. 技术文档 | 将OpenSCA接入GitHub Action,从软件供应链入口控制风险面

    继Jenkins和Gitlab CI之后,GitHub Action的集成也安排上啦~ 若您解锁了其他OpenSCA的用法,也欢迎向项目组来稿,将经验分享给社区的小伙伴们~ 参数说明 参数 是否必须 ...

  5. oracle表空间已满解决

    在日常的oralce使用中最长遇到的问题就是oralce的表空间满了,数据无法写入报错,这种情况下通常是磁盘没有足够的空间或者表空间的数据文件达到32G(linux最大限制单个文件不超过32G)无法继 ...

  6. 理解 docker volume

    1. docker volume 简介 文章 介绍了 docker image,它由一系列只读层构成,通过 docker image 可以提高镜像构建,存储和分发的效率,节省时间和存储空间.然而 do ...

  7. nohup 与 >/dev/null 与 2>&1 作用与区别

    转载请注明出处: 在 Linux 中,>/dev/null 和 2>&1 是两个常用的重定向操作,它们用于控制命令的输出和错误信息.而且这两个参数经常 与 nohup 命令一起使用 ...

  8. xshell配置隧道转移规则

    钢铁知识库,一个学习python爬虫.数据分析的知识库.人生苦短,快用python. xshell是什么 通俗点说就是一款强大ssh远程软件,可以方便运维人员对服务器进行管理操作,功能很多朋友们自行探 ...

  9. IDE-常用插件

    2021-8-25_IDE-常用插件 1. 背景 提升编写代码的舒适度,提升开发效率 2. 常用插件列表 IDE EVal Reset 白嫖付费的golang编辑器,reset插件可以重置golang ...

  10. [转帖]美国出口管制法律制度及中国企业风险防范——EAR核心内容解读

    http://bzy.scjg.jl.gov.cn/wto/zszc/myxgzs/202202/t20220221_636006.html 发布时间:2022-01-18 一.<美国出口管理条 ...