内存池是什么原理?|内存池简易模拟实现|为学习高并发内存池tcmalloc做准备

news2024/11/15 12:44:55

前言

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

这两个都是博主在学习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;
}

测试结果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Linux系统中如何搭建Apache服务

在Linux系统中如何搭建Apache服务 Apache服务是一种开源的、跨平台的Web服务器软件&#xff0c;它的作用是提供网页内容给用户的Web浏览器。具体来说&#xff0c;Apache服务有以下几个主要的作用&#xff1a; 1.Web服务器&#xff1a;Apache是一款功能强大且广泛使用的Web服务…

54、Mysql索引的数据结构,各自优劣

Mysql索引的数据结构&#xff0c;各自优劣 索引的数据结构和具体存储引擎的实现有关在MySQL中使用较多的索引有Hash索引&#xff0c;B树索引等InnoDB存储引擎的默认索引实现为: B树索引。对于哈希索引来说&#xff0c;底层的数据结构就是哈希表&#xff0c;因此在绝大多数需求…

C++编程(三)—— C++11

文章目录 绑定器和函数对象函数对象绑定器lambda表达式 关键词与语法autonullptr右值引用 智能指针容器set和mapunordered_set和unordered_map数组链表 语言级别支持的多线程编程thread子线程如何结束主线程如何处理子线程线程间的互斥线程的同步通信机制&#xff08;条件变量&…

Windows Cluster 分布式算法

在分布式系统中&#xff0c;都需要解决分布式一致性问题。那么&#xff0c;在Windows 集群中&#xff0c;使用了什么算法来保证集群的一致性呢——Paxos。Windows Server 故障转移集群 (WSFC) 使用 Paxos 算法在整个系统中同步更改。通过记录 Paxos Tag 值并保留历史记录&#…

连锁门店如何搭建B2B2C多用户商城系统?

现在很多的线下店铺都开始慢慢的转型线上了&#xff0c;想线上线下相结合&#xff0c;但是最近很多的商家都在问什么样的B2B2C商城系统开发适合线下店铺呢?这个问题今天加速度jsudo小编给大家一起整理如下&#xff0c;相信商家看完后就知道如何选择一款合适的商城系统了。 一、…

【C语言】求序列前N项和

问题描述 输入一个正整数n&#xff0c;输出2/13/25/38/5…的前n项之和&#xff08;该序列从第2项起&#xff0c;每一项的分子是前一项分子与分母的和&#xff0c;分母是前一项的分子&#xff09;&#xff0c;保留2位小数。试编写相应程序。 代码实现 #include<stdio.h>…

spring-14优化性能

原始Junit测试Spring的问题 这里的&#xff1a;第一行代码获得应用上下文对象&#xff0c;第二行代码获得你那个对象 2、原先测试找junit&#xff0c;现在测试找spring&#xff0c;然后再找&#xff0c;junit&#xff0c;告诉我配置文件是什么&#xff0c;然后注入测试对象&am…

LeetCode 172.阶乘后的零

基础方法就是暴力解&#xff0c;其次是数学归纳。 具体思路如下&#xff1a;&#xff08;copy大佬的&#xff09; 耐心看完&#xff01; 代码如下&#xff1a; #include <iostream> #include <vector> #include <cmath> #include <algorithm> #incl…

Android GridPager实战,从RecyclerView to ViewPager

这个简单的的案例展示了如何从RecyclerView to ViewPager&#xff0c;以网上的公开图片为样例。 安卓开发中从RecyclerView 到 ViewPager demo运行结果demo项目工程目录结构关键代码 MainActivity关键代码GridFragment关键代码ImageFragment关键代码ImagePagerFragment关键布局…

关于示波器引入50HZ工频干扰的解释

前几天&#xff0c;小白在实验室工作时&#xff0c;听到同事抱怨示波器有问题。上前查看&#xff0c;才知道&#xff0c;小白的那位同事在测量信号波形时&#xff0c;实际与理想相差甚远。于是乎&#xff0c;在探头什么也不接的情况下&#xff0c;发现示波器本身也存在波形信号…

2022Robocom国赛-u2 女王的大敕令

副本是游戏里的一个特色玩法&#xff0c;主要为玩家带来装备、道具、游戏资源的产出&#xff0c;满足玩家的游戏进程。 在 MMORPG《最终幻想14》里&#xff0c;有一个攻略人数最大达到 48 人的副本“零式贡希尔德神庙”&#xff0c;其中守关 BOSS “天佑女王”有一个很有趣的技…

图形编辑器开发:一些会用到的简单几何算法

大家好&#xff0c;我是前端西瓜哥。 开发图形编辑器&#xff0c;你会经常要解决一些算法问题。本文盘点一些我开发图形编辑器时遇到的简单几何算法问题。 矩形碰撞检测 判断两个矩形是否发生碰撞&#xff08;或者说相交&#xff09;&#xff0c;即两个矩形有重合的区域。 …

C生万物 | 程序员必备实用调试技巧分享

一起来学习调试~ 一、前言二、什么是Bug&#xff1f;三、调试是什么&#xff1f;有多重要&#xff1f;1、导学引入2、调试的基本步骤3、Debug和Release的介绍 四、Windows环境下VS调试介绍1、调试环境的准备2、学会快捷键3、调试的时候查看程序当前信息3.1 查看临时变量的值3.2…

基于SSM的购物商城系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

SpringBoot中通过自定义Jackson注解实现接口返回数据脱敏

场景 SpringBoot中整合Sharding Sphere实现数据加解密/数据脱敏/数据库密文&#xff0c;查询明文&#xff1a; SpringBoot中整合Sharding Sphere实现数据加解密/数据脱敏/数据库密文&#xff0c;查询明文_霸道流氓气质的博客-CSDN博客 上面讲的是数据库中存储密文&#xff0…

音乐格式转换mp3软件有哪些?分享三个方法给大家!

要将音乐文件转换为MP3格式的情况&#xff0c;以便在各种设备上播放。为了帮助大家完成这个任务&#xff0c;下面将分享三种实用的方法和工具&#xff0c;其中方法一是使用记灵在线工具进行音乐格式转换。无论您是音乐爱好者还是需要将音频文件转换为MP3格式的专业人士&#xf…

障碍物距离显示需求功能定义

1. 需求 来源 整车功能定义清单中SAF-10 -06 条需求 。泊车视觉辅助 &#xff0c;360全景 -距离树数值显示时&#xff0c;显示车辆与障碍物的距离数字 。 2. 开发 范围 项目带全景功能的配置&#xff0c;见最新配置表&#xff1a; 3. 功能 定义 在雷达报警激活状态下&#xff0…

SSM入门—Spring框架-IOC简单案例

目录 控制反转&#xff1a;创建对象 基于XML实现 基于注解实现 依赖注入&#xff1a;对象赋值 通过xml注入 控制反转&#xff1a;创建对象 把对象的创建权交给了Spring容器 基于XML实现 1、maven 导入依赖 <!--Maven会自动添加当前jar依赖的其他jar--> <depen…

第一章 数据库的操作

第一章 数据库的操作 一、库的操作1、创建数据库&#xff08;1&#xff09;语法&#xff08;2&#xff09;字符集与校验规则a.定义 &#xff08;3&#xff09;创建不同字符集与校验规则的数据库 2、查看数据库&#xff08;1&#xff09;语法&#xff08;2&#xff09;示例 3、显…

自定义类型——结构体,枚举,联合(详,真的太详了)

一.结构体 1.1什么是结构体&#xff0c;结构体如何声明和定义变量&#xff1f; 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 struct 变量名 { 不同类型的成员变量&#xff1b; }&#xff1b; 一定要记得在花括号的后面加上分…