doug lea malloc源码剖析之:sYSMALLOc
Posted ouyangbuxiu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了doug lea malloc源码剖析之:sYSMALLOc相关的知识,希望对你有一定的参考价值。
dlmalloc源码剖析之:sYSMALLOc
版权声明: 本文章由vt.buxiu发布在
www.vtzone.org,版权归vtzone研究小组所有,转载请保持此声明!!!
@@内容摘要:
sYSMALLOc函数用于合并fastbin中的空闲内存块,是doug lea malloc(dlmalloc)重要的函数之一。本文以dlmalloc2.7.0版本为基础,先以伪代码的形式介绍sYSMALLOc函数的主要流程。@@
@@内容摘要:
sYSMALLOc函数用于合并fastbin中的空闲内存块,是doug lea malloc(dlmalloc)重要的函数之一。本文以dlmalloc2.7.0版本为基础,先以伪代码的形式介绍sYSMALLOc函数的主要流程。@@
//
当malloc无法满足nb bytes大小请求时,调用sysmalloc向OS获取更多内存,因此av->top将被扩展
static Void_t * sYSMALLOc(INTERNAL_SIZE_T nb, mstate av)
... {
#if HAVE_MMAP //如果定义此宏,默认是定义了的,doug lea为windows也实现了一个模拟版本
//如果请求的大小nb达到了mmap分配阀值,并且通过mmap申请的内存块个数没有达到参数设置的上限
if ((unsigned long)(nb) >= (unsigned long)(av->mmap_threshold)
&&(av->n_mmaps < av->n_mmaps_max))
...{
//调整分配大小,多分配8+4个字节,并按pagesize对齐
size = (nb + SIZE_SZ + MALLOC_ALIGN_MASK + pagemask) & ~pagemask;
//调用mmap获得内存
mm = (char*)(MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE));
if(mm)...{//如果mmap分配成功
return chunk2mem(p);//将mmap获得内存返回给应用
}
//mmap如果失败,继续向下
}
#endif
/**//*没有mmap可用,或mmap不成功,或size每到达mapp阀值,则需要调整top*/
//将原来由top内存块信息记录下来
old_top = av->top;
old_size = chunksize(old_top);
old_end = (char*)(chunk_at_offset(old_top, old_size));
//调整请求的size
size = nb + av->top_pad + MINSIZ;
/**//*如果空间连续,先减去已经存在的top空间,再向操作系统申请*/
if (contiguous(av))
size -= old_size;
//调用sbrk向OS要求扩展heap空间
brk = (char*)(MORECORE(size));
//如果有mmap继续尝试通过mmap分配空间
#if HAVE_MMAP
if (brk == (char*)(MORECORE_FAILURE)) ...{
//不能和原来的brk分配空间连续了,单独分配,把之前的size修正
if (contiguous(av))
size = (size + old_size + pagemask) & ~pagemask;
//调用mmap申请空间
brk = (char*)(MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE));
//mmap申请成功
if (brk != (char*)(MORECORE_FAILURE)) ...{
snd_brk = brk + size;//记录申请得到的内存块尾部地址
//因为通过mmap扩展空间了,记录标记为top不再是连续的了
set_noncontiguous(av);
}
}
#endif
//如果是通过brk扩展内存成功的,则扩展top空间
if (brk == old_end && snd_brk == (char*)(MORECORE_FAILURE)) ...{
set_head(old_top, (size + old_size) | PREV_INUSE);
}
else//否则,即通过mmap(brk备份)分配成功的内存;或调用malloc之间有其他人调用了brk
...{//这时候需要调整top相关信息
front_misalign = 0;
end_misalign = 0;
correction = 0;
aligned_brk = brk;
if (contiguous(av)) ...{ //调用malloc之间有其他人调用了brk
//brk内存头部按8bytes对齐
front_misalign = (INTERNAL_SIZE_T)chunk2mem(brk) & MALLOC_ALIGN_MASK;
//将原来top剩余的size,算到新的brk扩展的空间内,多申请这么大
correction += old_size;
//brk内存尾部按pagesize对齐
end_misalign = (INTERNAL_SIZE_T)(brk + size + correction);
//计算因为头部对齐,和尾部对齐需要的额外空间大小
correction += ((end_misalign + pagemask) & ~pagemask) - end_misalign;
//多向操作系统申请因为对齐需要的内存
snd_brk = (char*)(MORECORE(correction));
}
else...{ //mmap分配的内存
//通过mmap扩展的内存,snd_brk指向mmap内存快的尾部
//通过mmap分配的不连续,在mmap请求前就已经按页对齐,所以这里不需要再调整对齐了
if (snd_brk == (char*)(MORECORE_FAILURE)) ...{
snd_brk = (char*)(MORECORE(0));
}
}
//av->top指向新扩展的头部,设置top->size
//av->top可能是原有top的扩展(连续)(这种情况不进这个分支)
//也可能是原来的top->size移过去,新的一块brk空间,与原来的不连续
//还有可能是来自一块mmap的空间头部
av->top = (mchunkptr)aligned_brk;
set_head(av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE);
av->sbrked_mem += correction;
/**//*如果不是第一次调用sysmalloc,程序走到这里说明通过mmap扩展,或中间有插入的brk
因此需要在原来的top后面插入边界,放上上面的top中合并操作合并了非自己管理的内存*/
if (old_size != 0) ...{//!=0说明不是第一次执行到这里
//插入边界标志块
chunk_at_offset(old_top, old_size)->size =
SIZE_SZ|PREV_INUSE;
//插入边界标志块
chunk_at_offset(old_top, old_size + SIZE_SZ)->size =
SIZE_SZ|PREV_INUSE;
//如果老的top空间还比较大,将其释放
if (old_size >= MINSIZE) ...{
fREe(chunk2mem(old_top));
}
}
}
p = av->top;
size = chunksize(p);
//从top分配一块nb大小的内存快满足应用需要
remainder_size = size - nb;
remainder = chunk_at_offset(p, nb);
av->top = remainder;
set_head(p, nb | PREV_INUSE);//最顶部着一块的p-bit肯定要设为inuse
set_head(remainder, remainder_size | PREV_INUSE);
return chunk2mem(p);
}
static Void_t * sYSMALLOc(INTERNAL_SIZE_T nb, mstate av)
... {
#if HAVE_MMAP //如果定义此宏,默认是定义了的,doug lea为windows也实现了一个模拟版本
//如果请求的大小nb达到了mmap分配阀值,并且通过mmap申请的内存块个数没有达到参数设置的上限
if ((unsigned long)(nb) >= (unsigned long)(av->mmap_threshold)
&&(av->n_mmaps < av->n_mmaps_max))
...{
//调整分配大小,多分配8+4个字节,并按pagesize对齐
size = (nb + SIZE_SZ + MALLOC_ALIGN_MASK + pagemask) & ~pagemask;
//调用mmap获得内存
mm = (char*)(MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE));
if(mm)...{//如果mmap分配成功
return chunk2mem(p);//将mmap获得内存返回给应用
}
//mmap如果失败,继续向下
}
#endif
/**//*没有mmap可用,或mmap不成功,或size每到达mapp阀值,则需要调整top*/
//将原来由top内存块信息记录下来
old_top = av->top;
old_size = chunksize(old_top);
old_end = (char*)(chunk_at_offset(old_top, old_size));
//调整请求的size
size = nb + av->top_pad + MINSIZ;
/**//*如果空间连续,先减去已经存在的top空间,再向操作系统申请*/
if (contiguous(av))
size -= old_size;
//调用sbrk向OS要求扩展heap空间
brk = (char*)(MORECORE(size));
//如果有mmap继续尝试通过mmap分配空间
#if HAVE_MMAP
if (brk == (char*)(MORECORE_FAILURE)) ...{
//不能和原来的brk分配空间连续了,单独分配,把之前的size修正
if (contiguous(av))
size = (size + old_size + pagemask) & ~pagemask;
//调用mmap申请空间
brk = (char*)(MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE));
//mmap申请成功
if (brk != (char*)(MORECORE_FAILURE)) ...{
snd_brk = brk + size;//记录申请得到的内存块尾部地址
//因为通过mmap扩展空间了,记录标记为top不再是连续的了
set_noncontiguous(av);
}
}
#endif
//如果是通过brk扩展内存成功的,则扩展top空间
if (brk == old_end && snd_brk == (char*)(MORECORE_FAILURE)) ...{
set_head(old_top, (size + old_size) | PREV_INUSE);
}
else//否则,即通过mmap(brk备份)分配成功的内存;或调用malloc之间有其他人调用了brk
...{//这时候需要调整top相关信息
front_misalign = 0;
end_misalign = 0;
correction = 0;
aligned_brk = brk;
if (contiguous(av)) ...{ //调用malloc之间有其他人调用了brk
//brk内存头部按8bytes对齐
front_misalign = (INTERNAL_SIZE_T)chunk2mem(brk) & MALLOC_ALIGN_MASK;
//将原来top剩余的size,算到新的brk扩展的空间内,多申请这么大
correction += old_size;
//brk内存尾部按pagesize对齐
end_misalign = (INTERNAL_SIZE_T)(brk + size + correction);
//计算因为头部对齐,和尾部对齐需要的额外空间大小
correction += ((end_misalign + pagemask) & ~pagemask) - end_misalign;
//多向操作系统申请因为对齐需要的内存
snd_brk = (char*)(MORECORE(correction));
}
else...{ //mmap分配的内存
//通过mmap扩展的内存,snd_brk指向mmap内存快的尾部
//通过mmap分配的不连续,在mmap请求前就已经按页对齐,所以这里不需要再调整对齐了
if (snd_brk == (char*)(MORECORE_FAILURE)) ...{
snd_brk = (char*)(MORECORE(0));
}
}
//av->top指向新扩展的头部,设置top->size
//av->top可能是原有top的扩展(连续)(这种情况不进这个分支)
//也可能是原来的top->size移过去,新的一块brk空间,与原来的不连续
//还有可能是来自一块mmap的空间头部
av->top = (mchunkptr)aligned_brk;
set_head(av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE);
av->sbrked_mem += correction;
/**//*如果不是第一次调用sysmalloc,程序走到这里说明通过mmap扩展,或中间有插入的brk
因此需要在原来的top后面插入边界,放上上面的top中合并操作合并了非自己管理的内存*/
if (old_size != 0) ...{//!=0说明不是第一次执行到这里
//插入边界标志块
chunk_at_offset(old_top, old_size)->size =
SIZE_SZ|PREV_INUSE;
//插入边界标志块
chunk_at_offset(old_top, old_size + SIZE_SZ)->size =
SIZE_SZ|PREV_INUSE;
//如果老的top空间还比较大,将其释放
if (old_size >= MINSIZE) ...{
fREe(chunk2mem(old_top));
}
}
}
p = av->top;
size = chunksize(p);
//从top分配一块nb大小的内存快满足应用需要
remainder_size = size - nb;
remainder = chunk_at_offset(p, nb);
av->top = remainder;
set_head(p, nb | PREV_INUSE);//最顶部着一块的p-bit肯定要设为inuse
set_head(remainder, remainder_size | PREV_INUSE);
return chunk2mem(p);
}
以上是关于doug lea malloc源码剖析之:sYSMALLOc的主要内容,如果未能解决你的问题,请参考以下文章
doug lea malloc源码剖析之:malloc_consolidate
doug lea malloc源码剖析之:sYSMALLOc
doug lea malloc源码剖析之:sYSMALLOc
doug lea malloc源码剖析之:sYSMALLOc