STL空间配置

2023-11-01

SGI STL有两级配置器:

  • 第一级配置器的allocate() 和 realloc()都是在调用malloc() 和 realloc()不成功后,改调用oom_malloc() 和 oom_realloc(),后两者都有内循环,不断调用“内存不足处理例程”,期望在某次调用之后,获得足够的内存来完成任务,但如果“内存不足处理例程”未被客端设定,oom_malloc() 和 oom_realloc()便调用__THROW_BAD_ALLOC,丢出bad_alloc异常信息,或利用exit(1)终止程序;

  • 第二级配置器多了一些机制,避免太多小额区块造成内存的碎片:

    • 如果区块够大,超过128bytes,就移交第一级配置器处理
    • 当区块小于128bytes,则以内存池管理,此法又称作次层配置:
      • 每次配置一大块内存,并维护对应的自有链表,下次再有相同大小的内存需求,就直接从free-list中拨出;
      • 如果客端释放小额区块,就由配置器回收到free-list中;

以下代码为简单实现:

第一级配置器头文件:malloc_alloc.h

#ifndef MALLOC_ALLOC_H
#define MALLOC_ALLOC_H
#include<stdlib.h>

class malloc_alloc{
private:
    //这两个函数处理内存不足的情况
    static void* oom_malloc(size_t n);
    static void* oom_realloc(void* p, size_t n);
public:
    static void* allocate(size_t n){
        void* result = malloc(n);
        if (result == 0)result = oom_malloc(n);
        return result;
    }

    static void deallocate(void* p, size_t n){
        free(p);   //n 其实完全没有用上
    }

    static void* reallocate(void* p, size_t old_sz, size_t new_sz){
        void* result = realloc(p, new_sz);
        if (result == 0)result = oom_realloc(p, new_sz);
        return result;
    }
};
#endif

第一级配置器头文件:malloc_alloc.cpp

#include"malloc_alloc.h"
#include<iostream>

void* malloc_alloc::oom_malloc(size_t n){
    void* result = 0;
    int i = 0, j = 0;

    //不停的尝试释放,配置,再释放,再配置
    for (; i < 100000; i++){
        j = 0;
        while (j < 100000)j++;

        result = malloc(n);
        if (result)return result;
    }

    std::cout << "malloc error!" << std::endl;
    return 0;
}

void* malloc_alloc::oom_realloc(void* p, size_t n){
    void* result = 0;
    int i = 0, j = 0;
    for (; i < 100000; i++){
        j = 0;
        while (j < 100000)j++;

        result = realloc(p, n);
        if (result)return result;
    }
    std::cout << "realloc error!" << std::endl;
    return 0;
}

第二级配置器头文件:memorypool.h

#ifndef _MEMORYPOOL_H
#define _MEMORYPOOL_H
#include<iostream>
using namespace std;

enum {ALIGN = 8}; //小型区块的上调边界
enum {MAX_BYTES = 128};  //小型区块的上限
enum {NUMFREELISTS = MAX_BYTES / ALIGN}; //freelists个数

//内存池
class Alloc{
private:
    //freelist的节点构造
    union obj{
        union obj* free_list_link;
        char client_data[1];
    };
private:
    //16个freelists
    static obj* volatile free_list[NUMFREELISTS];

    //以下函数根据区块的大小,决定使用第n号free_list,n从0开始
    static size_t FREELIST_INDEX(size_t bytes){
        return ((bytes + ALIGN - 1) / ALIGN - 1);   
    }

    //将bytes上调至8的倍数
    static size_t ROUND_UP(size_t bytes){
        return (((bytes)+ALIGN - 1) & ~(ALIGN - 1));
    }

    //返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list
    static void* refill(size_t n);

    //配置一大块区间,可容纳nobjs个大小为size的区块,如果修饰nobjs个区块无法满足,
    //nobjs可能会降低
    static char* chunk_alloc(size_t size, int& nobjs);

    //区块的状态
    static char* start_free;  //内存池的起始地址
    static char* end_free;    //内存池的结束地址
    static size_t heap_size;   

public:
    static void* allocate(size_t n); //分配指定大小的内存
    static void deallocate(void* p, size_t n);  //回收指定内存
    static void* reallocate(void* p, size_t old_sz, size_t new_sz); //重新分配内存
};
#endif

第二级配置器头文件:alloc.cpp

#include"malloc_alloc.h"
#include"memorypool.h"

//static成员的初值设定
char* Alloc::start_free = NULL;
char* Alloc::end_free = NULL;
size_t Alloc::heap_size = 0;
Alloc::obj* volatile Alloc::free_list[NUMFREELISTS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

//分配内存
void* Alloc::allocate(size_t n){
    obj* volatile *my_free_list;
    obj* result;

    //大于128就调用malloc_alloc
    if (n > 128)return malloc_alloc::allocate(n);

    //寻找16个free_list中最合适的一个
    my_free_list = free_list + FREELIST_INDEX(n);

    result = *my_free_list;
    if (result == 0){
        //没有可用的free_list,准备重新填充free_list
        void* r = refill(ROUND_UP(n));
        return r;
    }
    //调整free_list
    *my_free_list = result->free_list_link;
    return result;
}

//释放内存
void Alloc::deallocate(void* p, size_t n){
    obj* q = (obj*)p;
    obj* volatile* my_free_list;

    //大于128就调用malloc_deallocate
    if (n > 128){
        malloc_alloc::deallocate(p, n);
        return;
    }

    //寻找对应的区块
    my_free_list = free_list + FREELIST_INDEX(n);
    q->free_list_link = *my_free_list;
    *my_free_list = q;
}

//返回一个大小为n的对象,并且有时候会为适当的free_list增加节点,假设n已经适当上调至8的倍数
void* Alloc::refill(size_t n){
    int nobjs = 3;
    //调用chunk_alloc(),尝试取得nobjs个区块作为free_list的新节点,注意参数nobjs是pass by reference
    char* chunk = chunk_alloc(n, nobjs);
    obj* volatile* my_free_list;
    obj* result;
    obj *current_obj, *next_obj;
    int i;

    //如果只获得一个区块,这个区块就分配给调用者,free_list无新节点
    if (nobjs == 1)return (chunk);


    //否则准备调整free_list,纳入新节点
    my_free_list = free_list + FREELIST_INDEX(n);

    result = (obj*)chunk;  //这一块准备返回给客户端

    //以下导引free_list指向新分配的空间(取自内存池)
    *my_free_list = next_obj = (obj*)(chunk + n);

    //以下将free_list的各节点串接起来
    for (i = 1;; i++){
        current_obj = next_obj;
        next_obj = (obj*)((char*)next_obj + n);
        if (nobjs - 1 == i){
            current_obj->free_list_link = 0;
            break;
        }
        else{
            current_obj->free_list_link = next_obj;
        }
    }
    return result;
}

//内存池配置内存
//调用chunk_alloc(),尝试取得nobjs个区块作为free_list的新节点
//注意参数nobjs是pass by reference
char* Alloc::chunk_alloc(size_t size, int& nobjs){
    char* result;
    size_t total_bytes = size * nobjs;
    size_t bytes_left = end_free - start_free; //内存池剩余内存

    if (bytes_left >= total_bytes){
        //内存池剩余空间完全满足需求量
        result = start_free;
        start_free += total_bytes;
        return (result);
    }
    else if (bytes_left >= size){
        //内存池剩余空间不能满足总的需求量,但足够供应大于等于1个的区块
        nobjs = bytes_left / size;
        total_bytes = size * nobjs;
        result = start_free;
        start_free += total_bytes;
        return result;
    }
    else{
        //内存池剩余空间连一个区块的大小都无法提供s
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        //以下试着让内存池的残余零头还要利用价值
        //首先寻找释放的free_list
        if (bytes_left > 0){
            obj* volatile* my_free_list = free_list + FREELIST_INDEX(bytes_left);

            //调整free_list,将内存池中的剩余空间插入
            ((obj*)start_free)->free_list_link = *my_free_list;
            *my_free_list = (obj*)start_free;
        }

        //配置heap空间,用来补充内存池
        start_free = (char*)malloc(bytes_to_get);
        if (start_free == 0){
            //heap空间不足,malloc失败
            int i;
            obj* volatile *my_free_list, *p;
            //以下搜寻适当的free_list
            //所谓适当是指“尚有未用区块,且区块够大”的free_list
            for (i = size; i <= MAX_BYTES; i += ALIGN){
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (p != 0){
                    *my_free_list = p->free_list_link;
                    start_free = (char*)p;
                    end_free = start_free + i;
                    //递归调用自己,为了修正nobjs
                    return (chunk_alloc(size, nobjs));
                }
            }
            end_free = 0;
            start_free = (char*)malloc_alloc::allocate(bytes_to_get);
        }
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        //递归调用自己,为了修正nobjs
        return (chunk_alloc(size, nobjs));
    }
}

测试文件:main.cpp

#include"memorypool.h"
#include<vector>

int main(){

    Alloc pool;
    int* p = (int*)pool.allocate(sizeof(int)* 10);
    if (p == 0)cout << "allocate error." << endl;

    for (int i = 0; i < 10; i++)p[i] = i;
    for (int i = 0; i < 10; i++)cout << p[i] << " ";
    cout << endl;
    pool.deallocate(p, 40);

    system("pause");
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STL空间配置 的相关文章

  • Python-迭代器/生成器

    可调用对象 callable Python中的可调用对象包括以下七种 用户使用def或lambda语句创建的函数 使用C语言 CPython 实现的内置函数 如time strftime 和len 使用C语言实现的方法 如dict get
  • 类的sizeof(二)

    1 空类的sizeof是1 空类是指没有成员的类 类中的函数不占空间 除非是虚函数 如 class A public A A void fun sizeof A 是1 注 class A1 public A1 A1

随机推荐

  • AndroidStudio Gradle手动下载

    AndroidStudio Gradle自动下载巨慢 因此手动配置 下载好的压缩包和解压后的文件夹复制到gradle 2 14 1 all gt 8bnwg5hd3w55iofp58khbp6yv文件夹下 将gradle 2 14 1 al
  • Android开发APP门户界面设计(作业一

    Android开发APP门户界面设计 作业一 1 内容 请根据课程实操实现APP门户界面框架设计 至少包含4个tab页 能实现tab页之间的点击切换 2 技术 使用布局 layouts 和分段 fragment 对控件进行点击监听 一 项目
  • 机器学习-实验一

    实验一 逻辑回归 一 实验目的 加深对逻辑回归算法的理解和认识 掌握基于逻辑回归的二分类算法和基于 softmax 的多分类算法的设计方法 二 实验原理 先拟合决策边界 不局限于线性 还可以是多项式 再建立这个边界与分类的概率联系 从而得到
  • Javascript操作DOM事件对象

    一 给HTML元素添加事件的三种方法 1 在HTML的标签上使用onxx属性 如
  • Java语言实现稀疏数组

    稀疏数组 关于作者 作者介绍 博客主页 作者主页 简介 JAVA领域优质创作者 一名在校大三学生 在校期间参加各种省赛 国赛 斩获一系列荣誉 关注我 关注我学习资料 文档下载统统都有 每日定时更新文章 励志做一名JAVA资深程序猿 文章目录
  • QT添加lib库后提示 No rule to make target “xxx.lib“ needed by “xxx.exe“

    QT添加外部的lib库 首先右键项目 选择添加库 进行选择 这里加入的是静态库 添加库后编译一直报错No rule to make target xxx lib needed by xxx 查找资料后发现是pro文件中添加lib库的语句错误
  • C++安装库

    这里以安装libLAS为例 去github下载文件 https github com libLAS libLAS 在该文件下输入以下指令 mkdir build cd build cmake make sudo make install 主
  • JavaWeb(四) 请求(request)与响应(response)

    1 请求响应流程图 Request 请求对象 tomcat服务器为我们创建 使用这个对象获取请求相关的数据 父接口 ServletRequest 子接口 HttpServletRequest Response 响应对象 tomcat服务器为
  • OpenAI首席科学家最新访谈:对模型创业两点建议、安全与对齐、Transformer够好吗?...

    导读 OpenAI首席科学家Ilya Sutskever最近和他的朋友Sven Strohband进行了一次简短的对话 访谈中主要提及了以下几个问题 对深度学习的信仰 对AGI的畅想 Transformer够不够好 让人震惊的涌现能力 安全
  • videojs 销毁重新初始化问题及其他使用

    1 videojs 销毁 this myvideo videojs myvideo bigPlayButton false textTrackDisplay false posterImage true errorDisplay false
  • 火狐浏览器不能上网

    只有火狐浏览器不能上网 用windows edge可以正常上网 尝试了一些方案 例如关闭火狐的网络代理 排障模式 重新安装等依然不能上网 解决方案 这种情况重置下Winsock 方法 单击 开始 找到 Windows PowerShell
  • 初始化int类型data1[ ]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,14,16,18,20}先使用任意一种算法对其排序提示用户输入一个数字,再折半查找

    初始化int类型data1 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18 20 先使用任意一种算法对其排序提示用户输入一个数字 应用折半查找函数模板找出它的位置 include using
  • 一些计算机方面的感悟

    1 架构设计的本质是深入理解业务场景之后用工程经验做出最佳权衡 2 计算机解决问题其实没有任何奇技淫巧 它唯一的解决办法就是穷举 穷举所有可能性 算法设计无非就是先思考 如何穷举 然后再追求 如何聪明地穷举
  • Android项目针对libs(armeabi,armeabi-v7a,x86)进行平台兼容

    1 Android设备如何加载 so文件 不同CPU架构的Android手机加载时会在libs下找自己对应的目录 从对应的目录下寻找需要的 so文件 如果没有对应的目录 就会去armeabi下去寻找 如果已经有对应的目录 但是如果没有找到对
  • , trailing comma 逗号的问题

    PHP 数组元素最好加上逗号 因为可以方便其他人添加内容JAVASCRIPT 其实也应该加上逗号的 但可惜IE9以下不认 所以 可以不加逗号JSON JSON hates trailing commasPYTHON 希望尾部元素有逗号 转载
  • _mm_pause

    翻译自Intel指令 PAUSE指令提升了自旋等待循环 spin wait loop 的性能 当执行一个循环等待时 Intel P4或Intel Xeon处理器会因为检测到一个可能的内存顺序违规 memory order violation
  • 1.2冯•诺依曼模型

    文章目录 1 2 1 4个子系统 1 2 2 存储程序概念 1 2 3 指令的顺序执行 前一节中讲到的基于图灵机所建造的计算机是在存储器中存储数据 在1944 1945年期间 冯 诺依曼指出 程序和数据在逻辑上是相同的 因此程序也能存储在计
  • Mariadb主从复制之MHA配置

    Mariadb主从复制之MHA配置 一 环境介绍 1 主从复制及半同步复制配置链接 2 IP规划 二 检查一主两从数据库状态 1 主库状态 2 从库状态 三 MHA高可用介绍 1 MAH介绍 2 MAH作用 四 MHA基本环境配置 1 所有
  • linux线程使用

    概念 1 PCB Process Control Block 进程管理块 系统中存放进程的管理和控制信息的数据结构体 每一个进程均有一个PCB 在创建进程时建立 直到进程撤销而撤销 2 程序段 是进程中能被进程调度程序在CPU上执行的程序代
  • STL空间配置

    SGI STL有两级配置器 第一级配置器的allocate 和 realloc 都是在调用malloc 和 realloc 不成功后 改调用oom malloc 和 oom realloc 后两者都有内循环 不断调用 内存不足处理例程 期望