STL alloc源码分析 sgi-2.91版本 (二)

refill函数

分成两个部分
1.chunk_alloc函数负责切割出一大块内存空间
2.chunk_alloc()下面的代码负责对这片空间加工,切割成一个一个指定内存大小的内存块,并由free_list_link 连接起来。

/* Returns an object of size n, and optionally adds to size n free list.*/
/* We assume that n is properly aligned.                                */
/* We hold the allocation lock.                                         */
/*返回一个大小为n的对象,可以选择添加到大小为n的空闲列表中
/*我们假设n是正确对齐的。* /
/*我们持有分配锁。*/

template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)  //n已经8字节对齐
{
    int nobjs = 20;   //默认创建20个区块
    char * chunk = chunk_alloc(n, nobjs); 
    //检查是否能够创建20个,返回创建实际空间的首地址,详细分析见下面。
    //注意此处nobjs按引用传递,属于传入传出参数,返回值是实际能创建出的内存块
    obj * __VOLATILE * my_free_list;
    obj * result;
    obj * current_obj, * next_obj;
    int i;

    if (1 == nobjs) return(chunk);       //如果实际只要一块,直接返回
    my_free_list = free_list + FREELIST_INDEX(n);  //根据n指向相应区号表

    /* Build free list in chunk */
    /* 以下操作是把创建好的chunk空间分割成一个一个区块,每个区块由free_list_link相联系*/
      result = (obj *)chunk;    //分配好内存块的首地址
      *my_free_list = next_obj = (obj *)(chunk + n);  //区号链表指向第二个可用位置,因为第一个位置返回给容器使用
      for (i = 1; ; i++) {
        current_obj = next_obj;
        next_obj = (obj *)((char *)next_obj + n);   //(char*)保证+n个字节长度	
        if (nobjs - 1 == i) {          //到达区块最大数时退出
            current_obj -> free_list_link = 0;
            break;
        } else {
            current_obj -> free_list_link = next_obj;     //free_list_link 连接各个内存块
        }
      }
    return(result);   //返回分配好内存块的首地址
}

图来自侯捷老师:
在这里插入图片描述

chunk_alloc函数分析

if :优先判断备用池pool空间满足需求?
else:分配一大块空间
然后递归调用自己,每次备用池大小会发生变化,第二次必然跳出递归。

/* We allocate memory in large chunks in order to avoid fragmenting     */
/* the malloc heap too much.                                            */
/* We assume that size is properly aligned.                             */
/* We hold the allocation lock.                                         */
/*我们以大的块分配内存,以避免碎片化*/
/* malloc堆太多。* /
/*我们假设大小是正确对齐的。* /
/*我们持有分配锁。*/

template <bool threads, int inst>
char*
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
    char * result;
    size_t total_bytes = size * nobjs;     		//总共需要的字节数
    size_t bytes_left = end_free - start_free;  //内存池剩余的内存数,char*类型相减得字节数

    if (bytes_left >= total_bytes) {    		//剩余空间满足要求,则切割相应大小空间使用
        result = start_free;
        start_free += total_bytes;				//start_free 更新位置
        return(result);        					//返回这段切割内存空间首地址
        
    } else if (bytes_left >= size) {			//不满足总块数的需求,但至少满足申请的一块内存的需求,核心思想:能拿多少块就拿多少
        nobjs = bytes_left/size;				//nobjs为实际从备用池切割的块数,注意,nobjs引用传递,随着函数结束传出。
        total_bytes = size * nobjs;				//实际总字节数
        result = start_free;
        start_free += total_bytes;				//start_free 更新位置
        return(result);							//返回这段切割内存空间首地址
        
    } else {									//以上条件都不满足,malloc重新分配空间
    
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
		//计算需要malloc的总空间,ROUND_UP(heap_size >> 4)是16取余后8字节对齐,属于追加量

        // Try to make use of the left-over piece.
        if (bytes_left > 0) {				//备用池还剩空间,需要找人负责监管起来
            obj * __VOLATILE * my_free_list =
                        free_list + FREELIST_INDEX(bytes_left);  
						/*static  size_t FREELIST_INDEX(size_t bytes) {
       									 return (((bytes) + __ALIGN-1)/__ALIGN - 1);
  												向上取整思想					  }*/
						//为了防止内存碎片,需要找到负责这一小块内存的区号链表
						//例如剩28字节空间,(28+7)/8-1=3,这块内存交给#3号(负责32字节)区号链表负责
	
            ((obj *)start_free) -> free_list_link = *my_free_list;
			//*my_free_list存放当前区号链表指向的一块可用空间

            *my_free_list = (obj *)start_free;
            //当前区号链表指向的可用空间变为这块用不到的备用池空间
            //下次使用此区号链表就会优先使用这块空闲区域
        }
        start_free = (char *)malloc(bytes_to_get); //重新分配一块空间
        if (0 == start_free) {  				  //如果分配失败,比如系统空间不足,爆内存
            int i;
            obj * __VOLATILE * my_free_list, *p;
            // Try to make do with what we have.  That can't hurt.  We do not try smaller requests, since that tends to result in disaster on multi-process machines.
            //试着用我们现有的东西将就一下。不能伤害。我们不尝试较小的请求,因为这往往会导致多进程机器的灾难。
            
            for (i = size; i <= __MAX_BYTES; i += __ALIGN) {   		//8字节移动找区号链表,找可以借空间的区号链表大哥
                my_free_list = free_list + FREELIST_INDEX(i);      //i对应相应区号链表
                p = *my_free_list;				//p指向当前区号链表负责的一块可用空间
                if (0 != p) {					//大哥手里有钱(空间)
                    *my_free_list = p -> free_list_link;		//这块空间被小弟借走了,区号链表指向下一块可用空间
                    
                    start_free = (char *)p;
                    end_free = start_free + i;
					//这块空间成为新的备用池
					//因为是大哥(区号更大),那么在下面这个递归调用中
					//bytes_left >= size是必然的,下次递归可以正常返回result。

                    return(chunk_alloc(size, nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
          
	    	end_free = 0;	// In case of exception.分配内存失败,交给第一级分配器
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
            // This should either throw an exception or remedy the situation.  Thus we assume it succeeded.
            //这应该会抛出异常或纠正这种情况。因此我们认为它成功了。
        }
        
  		//malloc正常成功时
        heap_size += bytes_to_get;					//总字节数更新
        end_free = start_free + bytes_to_get;		//备用池尾部位置更新
        return(chunk_alloc(size, nobjs));			//递归调用自己,分配空间后下次递归就可以跳出,秒啊
    }
}

ROUND_UP字节对齐函数,__ALIGN=8,返回8字节对齐

static size_t ROUND_UP(size_t bytes) {
        return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));

分析几个if条件
(1)if (bytes_left >= total_bytes) //剩余空间满足要求,则切割相应大小空间使用
pool之前为2000字节,申请了20*88=1760个字节
在这里插入图片描述

(2)else if (bytes_left >= size) //不满足总块数的需求,但至少满足申请的一块内存的需求,核心思想:能拿多少块就拿多少
pool之前为640字节,申请了6410=640个字节
在这里插入图片描述
(3)else //以上条件都不满足,malloc重新分配空间
pool之前为80,申请104
2*20+RoundUp(5200>>4)空间
在这里插入图片描述

一些成员的初始化(补充)

template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = 0;

template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;

template <bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;

template <bool threads, int inst>
__default_alloc_template<threads, inst>::obj * __VOLATILE
__default_alloc_template<threads, inst> ::free_list[
# ifdef __SUNPRO_CC
    __NFREELISTS
# else
    __default_alloc_template<threads, inst>::__NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
// The 16 zeros are necessary to make version 4.1 of the SunPro compiler happy.  Otherwise it appears to allocate too little space for the array.
//要使SunPro 4.1版本的编译器满意,这16个零是必要的。否则,它似乎为数组分配的空间太少了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值