dlmalloc源码剖析之:mALLOc
Posted ouyangbuxiu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dlmalloc源码剖析之:mALLOc相关的知识,希望对你有一定的参考价值。
dlmalloc源码剖析之:mALLOc
版权声明: 本文章由vt.buxiu发布在
www.vtzone.org,版权归vtzone研究小组所有,转载请保持此声明!!!
@@内容摘要:这个函数应该是所有使用C/C++的人最熟悉的malloc调用的实现,c语言标准库提供的malloc函数.如果你使用linux, douglea malloc已经默认作为glibc的malloc,新的版本可能用的是ptmalloc(dlmalloc的多线程版本),如果你用的bsd4.2及以前系统libc用的kingsley的malloc; BSD(包括freebsd,netbsd,openbsd)4.2以后版本libc用的是PHKmalloc;如果你用的windows系统用的是microsoft的分配器算法;不过其他各个系统很容易使用doug lea malloc替换现有malloc函数.
本文以dlmalloc2.7.0版本为基础,先以伪代码的形式介绍sYSTRIm函数的主要流程。其中一些次要情节已略!@@
/*如果你使用linux, douglea malloc已经默认作为glibc的malloc,
新的版本可能用的是ptmalloc(dlmalloc的多线程版本)
如果你用的bsd4.2及以前系统libc用的kingsley的malloc;
BSD(包括freebsd,netbsd,openbsd)4.2以后版本libc用的是PHKmalloc;
如果你用的windows系统用的是microsoft的分配器算法;
不过其他各个系统很容易使用doug lea malloc替换现有的malloc*/
//c语言标准库提供的malloc函数;请注意malloc的几个return出口;
void* mALLOc(size_t bytes)
//0~4bytes->nb=16;>4bytes->nb=bytes+2个4字节头,然后对其到8bytes
checked_request2size(bytes, nb);
//如果在fastbin中有可用的块直接从fastbin中分配
if ((unsigned long)(nb) <= (unsigned long)(av->max_fast))
fb = &(av->fastbins[(fastbin_index(nb))]);
if ( (victim = *fb) != 0) //静态变量成员fastbin初始化为0
*fb = victim->fd;
check_remalloced_chunk(victim, nb);
return chunk2mem(victim);
//如果是<512bytes的小块请求,从smallbin中取一块
if (in_smallbin_range(nb))
//根据nb大小定位到smallbin
idx = smallbin_index(nb);
bin = bin_at(av,idx);
//如果该大小的bin[i]列表不为空
if ( (victim = last(bin)) != bin)
if (victim == 0) //静态变量成员smallbin初始化是0
malloc_consolidate(av);//第一次进来这里调用init_state函数进行初始化
else //有空闲块
/* victim
|
//
bin->first_chunk->chunk->chunk->...->last_chunk
| //
--------------------------------->|
*/
//按上图将victim从链表中删除,设置victim的下一块的pbit=inuse
//将victim块返回给应用
return chunk2mem(victim);
else //>512bytes,先释放fastbin中的块
idx = largebin_index(nb);
if (have_fastchunks(av)) //初始化的时候静态变量0,这个条件成立,
malloc_consolidate(av); //合并fastbin中的chunk,放入unsorted_bin
//这里是唯一将chunks放入bin的地方
//处理最近被释放或剩余的chunks,如果上次小请求没有完全匹配
//分割出小chunk就会发生
//最外面的for(;;)需要,因为我们无法知道在malloc结束前有合并操作
//因此需要多尝试一次,最多多循环一次
for(;;)
/*
unsorted chunks,所有的从一个chunk中分割出来的剩余chunk首先放到
unsorted chunks链表中,下次malloc调用中有一次被再次使用的机会。
作为一个队列维护。
当free或malloc_consolidate函数中 将剩余chunk放入unsorted chunks链表,
而在malloc函数中被分配或放入其他 正常bin中。
*/
//循环unsorted中每一块,与插入顺序相反,从后面开始匹配查找
while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av))
bck = victim->bk;//victim:unsorted's last chunk;bck:unsorted's last-two chunk
size = chunksize(victim);
if (in_smallbin_range(nb) //<512bytes
&& bck == unsorted_chunks(av) //unsorted队列中只有一块
&& victim == av->last_remainder //并且这一块是上一次分割剩下的
&& (unsigned long)(size) > (unsigned long)(nb + MINSIZE)) //剩余的chunk必须大于MINSIZE
//分割nb出去,剩余的继续放在reminder和unsorted中
return chunk2mem(victim);
//unsorted_bin中多于一块chunk,或者剩余一块但不是上一次分割剩余的
//或者剩余的一块大小太小,继续向下
//把最后一块从unsorted freelist中删除
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
//如果正好完全匹配,则return
if (size == nb)
set_inuse_bit_at_offset(victim, size);
check_malloced_chunk(victim, nb);
return chunk2mem(victim);
//不是完全匹配就从unsorted_bin移到normal_bin中
if(in_smallbin_range(size))
else//注意large-bin中的内存块是有序的,FIFO
//end while
//对于<512bytes的请求,使用best-fit策略查找当前bin
//注意当前bin是根据应用请求的size直接index定位到的bin
if (!in_smallbin_range(nb)) //best-fit
if ((victim = last(bin)) != bin //empty or //first最大,但也不能满足请求
&&(unsigned long)(first(bin)->size) >= (unsigned long)(nb))
//从后往前找,找到第一个满足请求的
while (((unsigned long)(size = chunksize(victim)) < (unsigned long)(nb)))
victim = victim->bk;
//如果剩余的大小<MINSIZE不进行分割,直接将该块返回给应用
if (remainder_size < MINSIZE)
return chunk2mem(victim);
else//分割该块,返回给应用,剩余的块 放入unsorted-bin
return chunk2mem(victim);
//如果unsorted-bin,当前bin都没有满足的,依次查找下一个更大的bin,直到找到一个满足的为止
for (;;)
//这里使用了bitmap技巧快速查找到匹配的large-bin,具体信息可以参考www.vtzone.org其他文章
//bit > map说明这个32位中bit后面的bin都是空
/*bit==0??啥意思,index%32==0
index->bit
32 ->1 2^0
1 ->2 2^1
2 ->4 2^2
...
31 -> 2^31
*/
if (bit > map || bit == 0) //bit怎么会是0??? //从下一个32开始
do
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top; //所有bin都找完了还没找到,跳到下面use_top
while ( (map = av->binmap[block]) == 0);
bin = bin_at(av, (block << BINMAPSHIFT));
bit = 1;//从第一位开始
//......
//如果找到一块满足的,将该块进行分割
//如果剩余的大小<MINSIZE不进行分割,直接将该块返回给应用
if (remainder_size < MINSIZE)
return chunk2mem(victim);
else//分割该块,返回给应用,剩余的块 放入unsorted-bin
return chunk2mem(victim);
//end for(;;),遍历normal-bin
//如果所有bin都没有可用的块
use_top:
//如果top足够大,从top取一块return给用户,修改top指针
if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
return chunk2mem(victim);
//上面入口处如果请求>512bytes会触发合并fastbin
//在这里:如果fastbins无法满足,smallbins也无法满足,
//而后合并fastbins放入unsorted_bins,
//对于大块再到unsorted_bins找,如果没有精确匹配放入normal_bin
//然后再到normal_bins找best-fit
//如果还没找到,扩展top
//由此可知,如果请求smallsize,则不会触发上述合并fastbins,
//然后会触发到unsorted_bins查找,只有一块上次剩余的小块才会
//被分配,或者精确匹配,否则放入normal_bins
//然后不管larger或small都到normal_bins中查找
//所以在这里对fastbins合并再尝试一次
else if (have_fastchunks(av))
assert(in_smallbin_range(nb));
malloc_consolidate(av);
idx = smallbin_index(nb); /* restore original bin index */
else //top也没法满足,向OS扩展内存
return sYSMALLOc(nb, av);
//最外层的for(;;)
//end
作者: vt.buxiu@www.vtzone.org
@@内容摘要:这个函数应该是所有使用C/C++的人最熟悉的malloc调用的实现,c语言标准库提供的malloc函数.如果你使用linux, douglea malloc已经默认作为glibc的malloc,新的版本可能用的是ptmalloc(dlmalloc的多线程版本),如果你用的bsd4.2及以前系统libc用的kingsley的malloc; BSD(包括freebsd,netbsd,openbsd)4.2以后版本libc用的是PHKmalloc;如果你用的windows系统用的是microsoft的分配器算法;不过其他各个系统很容易使用doug lea malloc替换现有malloc函数.
本文以dlmalloc2.7.0版本为基础,先以伪代码的形式介绍sYSTRIm函数的主要流程。其中一些次要情节已略!@@
/*如果你使用linux, douglea malloc已经默认作为glibc的malloc,
新的版本可能用的是ptmalloc(dlmalloc的多线程版本)
如果你用的bsd4.2及以前系统libc用的kingsley的malloc;
BSD(包括freebsd,netbsd,openbsd)4.2以后版本libc用的是PHKmalloc;
如果你用的windows系统用的是microsoft的分配器算法;
不过其他各个系统很容易使用doug lea malloc替换现有的malloc*/
//c语言标准库提供的malloc函数;请注意malloc的几个return出口;
void* mALLOc(size_t bytes)
//0~4bytes->nb=16;>4bytes->nb=bytes+2个4字节头,然后对其到8bytes
checked_request2size(bytes, nb);
//如果在fastbin中有可用的块直接从fastbin中分配
if ((unsigned long)(nb) <= (unsigned long)(av->max_fast))
fb = &(av->fastbins[(fastbin_index(nb))]);
if ( (victim = *fb) != 0) //静态变量成员fastbin初始化为0
*fb = victim->fd;
check_remalloced_chunk(victim, nb);
return chunk2mem(victim);
//如果是<512bytes的小块请求,从smallbin中取一块
if (in_smallbin_range(nb))
//根据nb大小定位到smallbin
idx = smallbin_index(nb);
bin = bin_at(av,idx);
//如果该大小的bin[i]列表不为空
if ( (victim = last(bin)) != bin)
if (victim == 0) //静态变量成员smallbin初始化是0
malloc_consolidate(av);//第一次进来这里调用init_state函数进行初始化
else //有空闲块
/* victim
|
//
bin->first_chunk->chunk->chunk->...->last_chunk
| //
--------------------------------->|
*/
//按上图将victim从链表中删除,设置victim的下一块的pbit=inuse
//将victim块返回给应用
return chunk2mem(victim);
else //>512bytes,先释放fastbin中的块
idx = largebin_index(nb);
if (have_fastchunks(av)) //初始化的时候静态变量0,这个条件成立,
malloc_consolidate(av); //合并fastbin中的chunk,放入unsorted_bin
//这里是唯一将chunks放入bin的地方
//处理最近被释放或剩余的chunks,如果上次小请求没有完全匹配
//分割出小chunk就会发生
//最外面的for(;;)需要,因为我们无法知道在malloc结束前有合并操作
//因此需要多尝试一次,最多多循环一次
for(;;)
/*
unsorted chunks,所有的从一个chunk中分割出来的剩余chunk首先放到
unsorted chunks链表中,下次malloc调用中有一次被再次使用的机会。
作为一个队列维护。
当free或malloc_consolidate函数中 将剩余chunk放入unsorted chunks链表,
而在malloc函数中被分配或放入其他 正常bin中。
*/
//循环unsorted中每一块,与插入顺序相反,从后面开始匹配查找
while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av))
bck = victim->bk;//victim:unsorted's last chunk;bck:unsorted's last-two chunk
size = chunksize(victim);
if (in_smallbin_range(nb) //<512bytes
&& bck == unsorted_chunks(av) //unsorted队列中只有一块
&& victim == av->last_remainder //并且这一块是上一次分割剩下的
&& (unsigned long)(size) > (unsigned long)(nb + MINSIZE)) //剩余的chunk必须大于MINSIZE
//分割nb出去,剩余的继续放在reminder和unsorted中
return chunk2mem(victim);
//unsorted_bin中多于一块chunk,或者剩余一块但不是上一次分割剩余的
//或者剩余的一块大小太小,继续向下
//把最后一块从unsorted freelist中删除
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
//如果正好完全匹配,则return
if (size == nb)
set_inuse_bit_at_offset(victim, size);
check_malloced_chunk(victim, nb);
return chunk2mem(victim);
//不是完全匹配就从unsorted_bin移到normal_bin中
if(in_smallbin_range(size))
else//注意large-bin中的内存块是有序的,FIFO
//end while
//对于<512bytes的请求,使用best-fit策略查找当前bin
//注意当前bin是根据应用请求的size直接index定位到的bin
if (!in_smallbin_range(nb)) //best-fit
if ((victim = last(bin)) != bin //empty or //first最大,但也不能满足请求
&&(unsigned long)(first(bin)->size) >= (unsigned long)(nb))
//从后往前找,找到第一个满足请求的
while (((unsigned long)(size = chunksize(victim)) < (unsigned long)(nb)))
victim = victim->bk;
//如果剩余的大小<MINSIZE不进行分割,直接将该块返回给应用
if (remainder_size < MINSIZE)
return chunk2mem(victim);
else//分割该块,返回给应用,剩余的块 放入unsorted-bin
return chunk2mem(victim);
//如果unsorted-bin,当前bin都没有满足的,依次查找下一个更大的bin,直到找到一个满足的为止
for (;;)
//这里使用了bitmap技巧快速查找到匹配的large-bin,具体信息可以参考www.vtzone.org其他文章
//bit > map说明这个32位中bit后面的bin都是空
/*bit==0??啥意思,index%32==0
index->bit
32 ->1 2^0
1 ->2 2^1
2 ->4 2^2
...
31 -> 2^31
*/
if (bit > map || bit == 0) //bit怎么会是0??? //从下一个32开始
do
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top; //所有bin都找完了还没找到,跳到下面use_top
while ( (map = av->binmap[block]) == 0);
bin = bin_at(av, (block << BINMAPSHIFT));
bit = 1;//从第一位开始
//......
//如果找到一块满足的,将该块进行分割
//如果剩余的大小<MINSIZE不进行分割,直接将该块返回给应用
if (remainder_size < MINSIZE)
return chunk2mem(victim);
else//分割该块,返回给应用,剩余的块 放入unsorted-bin
return chunk2mem(victim);
//end for(;;),遍历normal-bin
//如果所有bin都没有可用的块
use_top:
//如果top足够大,从top取一块return给用户,修改top指针
if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
return chunk2mem(victim);
//上面入口处如果请求>512bytes会触发合并fastbin
//在这里:如果fastbins无法满足,smallbins也无法满足,
//而后合并fastbins放入unsorted_bins,
//对于大块再到unsorted_bins找,如果没有精确匹配放入normal_bin
//然后再到normal_bins找best-fit
//如果还没找到,扩展top
//由此可知,如果请求smallsize,则不会触发上述合并fastbins,
//然后会触发到unsorted_bins查找,只有一块上次剩余的小块才会
//被分配,或者精确匹配,否则放入normal_bins
//然后不管larger或small都到normal_bins中查找
//所以在这里对fastbins合并再尝试一次
else if (have_fastchunks(av))
assert(in_smallbin_range(nb));
malloc_consolidate(av);
idx = smallbin_index(nb); /* restore original bin index */
else //top也没法满足,向OS扩展内存
return sYSMALLOc(nb, av);
//最外层的for(;;)
//end
作者: vt.buxiu@www.vtzone.org
以上是关于dlmalloc源码剖析之:mALLOc的主要内容,如果未能解决你的问题,请参考以下文章
doug lea malloc源码剖析之:malloc_consolidate
doug lea malloc源码剖析之:malloc_consolidate