STL空间配置

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值