C++的allactor

news/2025/2/25 23:41:56

https://zhuanlan.zhihu.com/p/693267319

1 双层内存配置器

SGI设计了两层的配置器,也就是第一级配置器和第二级配置器。同时为了自由选择,STL又规定了 __USE_MALLOC 宏,如果它存在则直接调用第一级配置器,不然则直接调用第二级配置器。SGI未定义该宏,也就是说默认使用第二级配置器。

1.1 一级内存配置器

直接调用malloc和free来配置释放内存,简单明了。

template<int Inst>
class __MallocAllocTemplate //一级空间配置器
{
    typedef void (*OOM_HANDLER)();
private:
    //these funs below are used for "OOM" situations
    //OOM = out of memory
    static void* OOM_Malloc(size_t n); //function
    static void* OOM_Realloc(void *p, size_t newSZ); //function
    static OOM_HANDLER OOM_Handler; //function pointer

public:
    static void* Allocate(size_t n)
    {
        void* ret = malloc(n);
        if (ret == NULL)
            ret = OOM_Malloc(n);
        return ret;
    }

    static void Deallocate(void* p, size_t n)
    {
        free(p);
    }

    static void* Reallocate(void* p, size_t oldSZ, size_t newSZ)
    {
        void* ret = realloc(p, newSZ);
        if (ret == NULL)
            ret = OOM_Realloc(p, newSZ);
        return ret;
    }
    //static void (* set_malloc_handler(void (*f)()))()
    //参数和返回值都是函数指针void (*)()
    static OOM_HANDLER SetMallocHandler(OOM_HANDLER f)
    {
        OOM_HANDLER old = OOM_Handler;
        OOM_Handler = f;
        return old;
    }
};

//让函数指针为空
template<int Inst>
void (*__MallocAllocTemplate<Inst>::OOM_Handler)() = NULL;

template<int Inst>
void* __MallocAllocTemplate<Inst>::OOM_Malloc(size_t n)
{
    void* ret = NULL;
    void(*myHandler)() = NULL;
    for (;;)
    {
        myHandler = OOM_Handler;
        if (myHandler == NULL)
            throw bad_alloc();
        (*myHandler)();
        ret = malloc(n);
        if (ret != NULL)
            return ret;
    }
}

template<int Inst>
void* __MallocAllocTemplate<Inst>::OOM_Realloc(void* p, size_t newSZ)
{
    void* ret = NULL;
    void(*myHandler)() = NULL;
    for (;;)
    {
        myHandler = OOM_Handler;
        if (myHandler == NULL)
            throw bad_alloc();
        (*myHandler)();
        ret = realloc(p, newSZ);
        if (ret != NULL)
            return ret;
    }
}


typedef __MallocAllocTemplate<0> MallocAlloc; //一级空间配置重命名

1.2 二级内存配置器

1.如果用户需要的区块大于128,则直接调用第一级空间配置器
2.如果用户需要的区块大于128,则到自由链表中去找

  • 如果自由链表有,则直接去取走
  • 不然则需要装填自由链表(Refill)

1.2.1 自由链表

在这里插入图片描述
自由链表是一个指针数组,它的数组大小为16,每个数组元素代表所挂的区块大小,比如free _ list[0]代表下面挂的是8bytes的区块,free _ list[1]代表下面挂的是16bytes的区块…….依次类推,直到free _ list[15]代表下面挂的是128bytes的区块

同时我们还有一个被称为内存池地方,以start _ freeend _ free记录其大小,用于保存未被挂在自由链表的区块,它和自由链表构成了伙伴系统。

1.2.2 工作原理

如果用户需要是一块n字节的区块,且n <= 128(调用第二级配置器),此时Refill填充是这样的:

  • 系统会自动将n字节扩展到8的倍数,再将RoundUP(n)传给Refill
  • 用户需要n字节,且自由链表中没有,因此系统会向内存池申请nobjs * n大小的内存块,默认nobjs=20
  • 如果内存池大于 nobjs * n,那么直接从内存池中取出
  • 如果内存池小于nobjs * n,但是比一块大小n要大,那么此时将内存最大可分配的块数给自由链表,并且更新nobjs为最大分配块数x (x < nobjs)
  • 如果内存池连一个区块的大小n都无法提供,那么首先先将内存池残余的零头给挂在自由链表上,然后向系统heap申请空间,申请成功则返回,申请失败则到自己的自由链表中看看还有没有可用区块返回
  • 如果连自由链表都没了最后会调用一级配置器。

3 自定义内存配置器

// TODO:为vector写一个内存配置器,当空间大于1000时直接从堆分配内存


#include <memory>
#include <vector>
#include <iostream>
 
// 自定义内存配置器
template <typename T>
class CustomAllocator : public std::allocator<T> {
public:
    // 分配内存
    T* allocate(std::size_t n) {
        if (n * sizeof(T) > 1000) {
            // 大于1000字节时,使用堆分配
            return static_cast<T*>(::operator new(n * sizeof(T)));
        } else {
            // 小于或等于1000字节时,使用默认分配
            return std::allocator<T>::allocate(n);
        }
    }
 
    // 释放内存
    void deallocate(T* p, std::size_t n) {
        if (n * sizeof(T) > 1000) {
            // 大于1000字节时,使用堆释放
            ::operator delete(p);
        } else {
            // 小于或等于1000字节时,使用默认释放
            std::allocator<T>::deallocate(p, n);
        }
    }
};
 
int main() {
    // 使用自定义内存配置器的vector
    std::vector<int, CustomAllocator<int>> vec;
    vec.push_back(1); // 应该使用默认分配方式(通常很小)
    vec.push_back(2); // 同上
    vec.resize(1024); // 应该使用堆分配方式(因为1024 * sizeof(int) > 1000)
    vec.resize(1); // 应该再次使用默认分配方式(因为1 * sizeof(int) <= 1000)
    return 0;
}

http://www.niftyadmin.cn/n/5866969.html

相关文章

华为数通 HCIP-Datacom H12-831 新题

2024年 HCIP-Datacom&#xff08;H12-831&#xff09;变题后的新题&#xff0c;完整题库请扫描上方二维码&#xff0c;新题在持续更新中。 某台IS-IS路由器自己生成的LSP信息如图所示&#xff0c;从LSP信息中不能推断出以下哪一结论? A&#xff1a;该路由器某一个接口的IPv6地…

本地VSCode远程连wsl2中的C++环境的开发配置指南

请参考上一遍文章&#xff1a;在windows上安装wsl2&#xff0c;在wsl2中配置C开发环境-CSDN博客

量子计算如何改变加密技术:颠覆与变革的前沿

量子计算如何改变加密技术:颠覆与变革的前沿 大家好,我是Echo_Wish,一名专注于人工智能和Python的自媒体创作者。今天,我们来探讨一个前沿且引人深思的话题——量子计算如何改变加密技术。随着量子计算的快速发展,传统的加密技术面临前所未有的挑战和机遇。本文将详细介绍…

算法(四)——动态规划

文章目录 基本思想适用条件最优子结构子问题重叠状态转移方程 解题步骤应用斐波那契数列背包问题最大子数组和 基本思想 动态规划的核心思想在于将一个复杂的问题分解为一系列相互关联的子问题&#xff0c;通过求解子问题并保存其解&#xff0c;避免对相同子问题的重复计算&am…

回合制游戏文字版(升级)

//在上一篇博客的基础上&#xff0c;加了细节的改动 //改动&#xff1a;添加了外貌&#xff0c;性别&#xff0c;招式的细节描绘&#xff1b;添加了个人信息展示界面 //一创建java文件1&#xff0c;命名为playGame package test2;import java.util.Random;public class play…

PHP入门基础学习四(PHP基本语法)

运算符 运算符&#xff0c;专门用于告诉程序执行特定运算或逻辑操作的符号。根据运算符的作用&#xff0c;可以将PHP语言中常见的运算符分为9类 算数运算符&#xff1a; 是用来处理加减乘除运算的符号 也是最简单和最常用的运算符号 赋值运算符 1. 是一个二元运算符&#x…

LabVIEW齿轮箱故障分析系统

在运维过程中&#xff0c;某大型风电场发现多台2.5MW风力发电机组在低速重载工况下频繁出现异常振动&#xff0c;导致齿轮箱温度升高和发电效率下降。传统的FFT频谱分析无法准确定位故障源&#xff0c;人工排查耗时且成本高昂。经初步检查&#xff0c;怀疑是行星齿轮箱内齿圈局…

常用的HTML meta标签有哪些

meta是 HTML 中的一个元数据标签&#xff0c;位于 <head> 标签内&#xff0c;不会在页面上直接显示&#xff0c;但能为浏览器和搜索引擎提供关于网页的重要信息。以下是一些常用的 <meta> 标签及其用途&#xff1a; 1. 字符编码声明 用于指定 HTML 文档的字符编码…