stl alloc 空间分配器 代码解析

这里是对https://github.com/zouxiaohang/TinySTL中的alloc的进行解析

下面这个仓库写的真好,文中的很多信息都是来自于该仓库
参考:https://github.com/steveLauwh/SGI-STL

解析stl是一种对艺术的享受

在计算机的发展中,内存一直是最重要的三个部分之一,而内存面临的主要问题,就在于如何进行高效的分配,以及内存碎片化问题,为了在一定程度上解决这些问题,stl中提供了一种方案,这就是空间配置器

空间配置器,分为两级,

  • 当配置区块超过 128 bytes,就使用第一级配置器
  • 当配置区块小于 128 bytes,使用内存池管理

空间配置器包含,动态空间配置、空间管理、空间释放

那么这两级是如何设计的呢?

void *alloc::allocate(size_t bytes){
        // EMaxBytes::MAXBYTES = 128
        /*
        这里大于128,就直接调用malloc
        */
		if (bytes > EMaxBytes::MAXBYTES){
			return malloc(bytes);
		}
		// 小于128,
		/*
		// 根据区块大小,决定使用第n号free-list, n从0开始计算
		该函数用来计算当前的链表节点序号
        static size_t FREELIST_INDEX(size_t bytes) {
            // return ((bytes + 8 - 1)/8 - 1);
            return ((bytes + EAlign::ALIGN - 1)/EAlign::ALIGN - 1);
        }
        对于碎片化的内存,配置器中使用的是链表来存储可用的空间
        可以看下面的图片,可以更好的理解存储过程
		*/
		size_t index = FREELIST_INDEX(bytes);
		obj *list = free_list[index];
		if (list){//此list还有空间给我们
			free_list[index] = list->next;
			return list;
		}
		else{
		    //此list没有足够的空间,需要从内存池里面取空间
		    /*
		    // 将bytes 上调至8的倍数
            static size_t ROUND_UP(size_t bytes) {
                 return ((bytes + EAlign::ALIGN - 1) & ~(EAlign::ALIGN - 1));
            }
		    */
			return refill(ROUND_UP(bytes));
		}
	}

free-list 是指针数组,16 个数组元素,就是 16 个 free-list,各自管理大小分别为 8, 16, 24, 32,…128 bytes(8 的倍数)的小额区块。

小额区块的结构体 union _Obj 使用链表连接起来
在这里插入图片描述
用到的变量声明

static char *start_free;//内存池起始位置
static char *end_free;//内存池结束位置
char *alloc::start_free = 0;
char *alloc::end_free = 0;
size_t alloc::heap_size = 0;

alloc::obj *alloc::free_list[alloc::ENFreeLists::NFREELISTS] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

//free-lists的节点构造
union obj{
	union obj *next;
	char client[1];
};
static obj *free_list[ENFreeLists::NFREELISTS];

如果在链表中有剩余的空间,那么就直接取用,但如果没有足够的剩余呢?
那就要调用一下下面这个函数

// 返回一个大小为n的对象,并且有时候会为适当的free_list增加节点
// 假设bytes已经上调为8的倍数
void* alloc::refill(size_t bytes) {
    // ENObjs::NOBJS = 20;
    size_t nobjs = ENObjs::NOBJS;
    // 该函数的解析在下面
    char *chunk = chunk_alloc(bytes, nobjs);
    obj **my_free_list = 0;
    obj *result = 0;
    obj *current_obj = 0, *next_obj = 0;
    if (nobjs == 1) {
        // 取出空间只够一个对象使用
        return chunk;
    } else {
        my_free_list = free_list + FREELIST_INDEX(bytes);
        result = (obj*)(chunk);
        *my_free_list = next_obj = (obj*)(chunk + bytes);
        // 将取出多余的空间加入到相应的free-list里面去
        for (int i = 1; ; ++i) {
            current_obj = next_obj;
            next_obj = (obj*)((char*)next_obj + bytes);
            if (nobjs - 1 == i) {
                current_obj->next = 0;
                break;
            } else {
                current_obj->next = next_obj;
            }
        }
        return result;
    }
}
//假设bytes已经上调为8的倍
char *alloc::chunk_alloc(size_t bytes, size_t& nobjs){
    char *result = 0;
    // 需要的块大小和相应的块数目
    size_t total_bytes = bytes * nobjs; // 需要申请的空间大小
    // 通过start_free 和 end_free , 初始状态下两者都是0,说明空间为空
    size_t bytes_left = end_free - start_free; // 计算内存池剩余空间

    if (bytes_left >= total_bytes){
        // 内存池剩余空间完全满足需要
        result = start_free;
        // 开始指针移动,表示该段空间已使用
        start_free = start_free + total_bytes;
        return result;
    } else if (bytes_left >= bytes){
        // 内存池剩余空间不能完全满足需要,但足够供应一个或以上的区块
        // 计算可以供给的内存块数目,这里的两步是为了计算8的整数块
        nobjs = bytes_left / bytes;
        total_bytes = nobjs * bytes;
        result = start_free;
        // 表明空间已使用
        start_free += total_bytes;
        return result;
    } else {
        // 内存池剩余空间连一个区块的大小都无法提供
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // 内存池的剩余空间分给合适的空闲链表
        if (bytes_left > 0){
            // 将剩余的内存块,插入到合适大小的内存块链表中
            obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
            ((obj *)start_free)->next = *my_free_list;
            *my_free_list = (obj *)start_free;
        }
        // 配置heap空间,用来补充内存池
        start_free = (char *)malloc(bytes_to_get);
        if (!start_free){
            // heap 空间不足,malloc 失败
            obj **my_free_list = 0, *p = 0;
            // 0 ~ 128 , 8
            for (int i = 0; i <= EMaxBytes::MAXBYTES; i += EAlign::ALIGN){
                // 遍历空闲链表
                /*
                因为之前的分配要的太多了,所以就从更小内存块申请
                */
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (p != 0){
                    // 空闲链表节点有效
                    *my_free_list = p->next;
                    // 单个内存块大小
                    start_free = (char *)p;
                    end_free = start_free + i;
                    return chunk_alloc(bytes, nobjs);
                }
            }
            // 内存空间清零
            end_free = 0;
        }
        // 堆大小
        heap_size += bytes_to_get;
        // 扩展空间
        end_free = start_free + bytes_to_get;
        return chunk_alloc(bytes, nobjs);
    }
}

释放内存

void alloc::deallocate(void *ptr, size_t bytes){
        // > 128, 释放由malloc分配的内存
		if (bytes > EMaxBytes::MAXBYTES){
		    // 释放申请的大块内存
			free(ptr);
		}
		else{
		    // 碎片内存,重新归入内存池
			size_t index = FREELIST_INDEX(bytes);
			obj *node = static_cast<obj *>(ptr);
			node->next = free_list[index];
			free_list[index] = node;
		}
	}
	// 重新分配,释放旧大小,申请新的大小
	void *alloc::reallocate(void *ptr, size_t old_sz, size_t new_sz){
		deallocate(ptr, old_sz);
		ptr = allocate(new_sz);

		return ptr;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值