写一个无碎片的malloc,求意见

HULIHONG 2012-09-13 07:16:04
需要写一个无碎片的malloc,大家给点意见吧,谢谢。
...全文
638 27 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2012-09-15
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 的回复:]

引用 23 楼 的回复:

引用 21 楼 的回复:

咱们平时所用的malloc及tcmalloc是通过什么算法将一大块内存分成若干种类的小块,比如32B 64B 256B等,这些种类的小块各自有多少个呢?是通过什么算法来实现的?谢谢。


概念错误。malloc并不只有所谓内存池,所谓内存池只是堆的前端,后端还是标准的堆算法。


能给一个后端的标准算法链接吗?谢谢
……
[/Quote]
给你一个我写的吧。
算法思想:
将所有空闲块以环状链表形式标注出来,开始的时候,只存在一个空闲块,就是此堆的全部空间。分配一个块,需要从空闲表里遍历,直到发现一个可以容纳的块,然后将此块分割为一个非空闲块(已分配的)和一个空闲块,接着调整链表。

struct heap_block_s
{
u_int32 closer;
u_int32 region;
u_int32 length;
};

struct heap_block
{
struct heap_block_s sblock;
u_int32 flo;
u_int32 fro;
};

struct heap_header
{
u_int32 user;
u_int32 size;
u_int32 cursor;
};

结构heap_block_s是基本结构,无论此块是否空闲,都存在这个结构。heap_header只有此块空闲时,才使用此结构,此结构也包含heap_block_s。
heap_block_s的三个域分别为:到前一个块的偏移,到堆顶的偏移,到下一个块的偏移(即此块的长度)。
使用这个结构,所有块便形成“相邻”的关系,就是说,可以通过一个块找到前一个及后一个相邻的块。

heap_block的2个附加域:空闲链表的左指针,空闲链表的右指针。

实现如下:

#define __heap_pool(hdr) ((void*)(((struct heap_header *)(hdr)) + 1))
#define __ptr_distance(hdr, p) ((u_int32)(((unsigned long)(p)) - ((unsigned long)(hdr))))
#define __heap_block(hdr, offs) ((struct heap_block *)(((unsigned char*)(hdr)) + (offs)))
#define __heap_cursor(hdr) __heap_block(hdr, hdr->cursor)


static inline struct heap_header *
__heap_header( struct heap_block_s * blk )
{ return (struct heap_header *)( (unsigned char *)blk - blk->region ); }

struct heap_header * heap_heap_header( void *p )
{
struct heap_block_s *n = (struct heap_block_s *)p - 1;
return __heap_header(n);
}

static void __heap_take_away( struct heap_header *hdr, struct heap_block *n )
{
struct heap_block *l;

l = (struct heap_block*)((unsigned char*)hdr + n->flo);

if(l == n)
hdr->cursor = 0;
else
{
struct heap_block *r;
u_int32 rr;

rr = n->fro;
r = __heap_block(hdr, rr);

l->fro = rr;
r->flo = n->flo;
if(hdr->cursor == __ptr_distance(hdr, n))
hdr->cursor = rr;
}
}

static void __heap_take_into( struct heap_header *hdr, struct heap_block *n )
{
u_int32 p = __ptr_distance(hdr, n);
if(hdr->cursor)
{
struct heap_block *cur, *r;

cur = __heap_cursor(hdr);
r = __heap_block(hdr, cur->fro);

n->flo = r->flo;
n->fro = cur->fro;
cur->fro = p;
r->flo = p;
}
else
{
n->flo = p;
n->fro = p;
hdr->cursor = __ptr_distance(hdr, n);
}
}

struct heap_header * heap_init_header( void * p, u_int32 sz, u_int32 usr )
{
struct heap_header *hdr;
struct heap_block *cur;

hdr = (struct heap_header *)p;
hdr->user = usr;
hdr->size = sz;
hdr->cursor = sizeof(struct heap_header);

cur = __heap_cursor(hdr);
cur->sblock.closer = 0;
cur->sblock.region = 0;
cur->sblock.length = sz - sizeof(struct heap_header);
cur->flo = hdr->cursor;
cur->fro = hdr->cursor;
return hdr;
}

void *heap_alloc( struct heap_header * hdr, u_int32 sz )
{
if(hdr->cursor)
{
struct heap_block_s *n = 0;
struct heap_block *c, *cur;
u_int32 tsz;

sz = cua_align(sz, sizeof(u_int32));
tsz = sz + sizeof(struct heap_block);

c = cur = __heap_cursor(hdr);
for(;;)
{
if(cur->sblock.length >= tsz)
{
// shoot it
u_int32 ss = cur->sblock.length - tsz;

if(ss < sizeof(struct heap_block))
{
n = & cur->sblock;
__heap_take_away(hdr, cur);
}
else
{
struct heap_block *nn;

n = (struct heap_block_s *)((unsigned char *)cur + ss);
n->closer = ss;
n->length = tsz;

cur->sblock.length = ss;

// fill closer of the next one
nn = (struct heap_block *)((unsigned char *)n + tsz);
if( __ptr_distance(hdr, nn) < hdr->size )
nn->sblock.closer = tsz;
}
n->region = __ptr_distance(hdr, n);
break;
}

// move cursor to point next block
hdr->cursor = cur->fro;
cur = __heap_block(hdr, cur->fro);

if(c == cur)
break;
}
if(n)
return n + 1;
}
return 0;
}

void heap_dealloc( void * nsp )
{
if(nsp)
{
bool f = false;
bool ft = false;
struct heap_header *hdr;
struct heap_block *ln, *n;

n = (struct heap_block *)((struct heap_block_s *)nsp - 1);
hdr = __heap_header( & n->sblock );

if(n->sblock.closer)
{
// get the prev node
struct heap_block *pn = (struct heap_block *)((unsigned char *)n - n->sblock.closer);

// check if the prev node is cua_free
if( pn->sblock.region == 0 )
{
// yes, the prev one is cua_free
// combine with the prev one
pn->sblock.length += n->sblock.length;
n = pn;
f = true;
}
}

// get the next node
ln = (struct heap_block *)((unsigned char *)n + n->sblock.length);
if( __ptr_distance(hdr, ln) < hdr->size )
{
// check if the next node is cua_free
if(ln->sblock.region == 0)
{
// yes, the next one is cua_free
n->sblock.length += ln->sblock.length;
__heap_take_away(hdr, ln);
ft = true;
}
}

if(f || ft)
{
// fill closer of the next one
struct heap_block *nn = (struct heap_block *)((unsigned char *)n + n->sblock.length);
if( __ptr_distance(hdr, nn) < hdr->size )
nn->sblock.closer = n->sblock.length;
}

if(!f)
__heap_take_into(hdr, n);

n->sblock.region = 0;
}
}

bool heap_debug_check( struct heap_header * hdr )
{
u_int32 closer = 0;
struct heap_block_s * n = (struct heap_block_s *) __heap_pool(hdr);
for( ; (unsigned char*)n < (unsigned char*)hdr + hdr->size; )
{
if(n->closer != closer)
{
assert(false);
return false;
}
closer = n->length;
n = (struct heap_block_s *)((unsigned char*) n + closer);
}
return true;
}

我测试过此算法,速度很快。

qq120848369 2012-09-14
  • 打赏
  • 举报
回复
内存池绝大多数时候是没用的,除非你的服务跑上N年不停机.
yantao473 2012-09-14
  • 打赏
  • 举报
回复
设x为你要分配的块,y为你设定的块的大小

x += y - (x%y);
mujiok2003 2012-09-14
  • 打赏
  • 举报
回复
boost::pool,上手很容易。自己写容易出错且费时费力。

http://www.boost.org/doc/libs/1_51_0/libs/pool/doc/html/index.html
redleaves 2012-09-14
  • 打赏
  • 举报
回复
同样大小的对象放到同一个池里,肯定不会产生碎片...
没事写分配器的人很多.什么tcmalloc,jemallco,nedmalloc之类的...你可以随便选个来用.
不过这种通用型的分配器,在某些方面肯定比不上专用分配器.

另外,除非是支持内存整理的gc.否则你再怎么写,通用型的分配器都避免不了碎片.无非是把碎片从别人手中转移到自己手中罢了.
但C/C++中的栈分配机制对gc有巨大阻力,更不用说内存整理了...

建议还是参考一下MC++D中的小对象分配器,做专用的内存分配器吧.
IVERS0N 2012-09-14
  • 打赏
  • 举报
回复
可以参考下STL的空间配置器 《STL源码剖析》第二章
HULIHONG 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

重复造轮子?不,这不是造轮子,是在造螺钉。
20多年前的C89里就有malloc函数了,为什么不用呢?

当然了,如果楼主的公司是写编译器的,就两说了!
[/Quote]

公司不是嫌有碎片吗,所以要自己写个,分配时候一整块的给就可以了。
mymtom 2012-09-14
  • 打赏
  • 举报
回复
重复造轮子?不,这不是造轮子,是在造螺钉。
20多年前的C89里就有malloc函数了,为什么不用呢?

当然了,如果楼主的公司是写编译器的,就两说了!
nice_cxf 2012-09-14
  • 打赏
  • 举报
回复
就是一个简易的内存池
至于越界或无效直接给assert就可以,这个显然应该是使用者的问题,给assert让他去调试
AnYidan 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

引用楼主 的回复:
需要写一个无碎片的malloc,大家给点意见吧,谢谢。


领导安排的工作,让我写一个公司内部自己使用的,初步想法是每块固定长度128B,小于128B就直接就给一个完整的块,比如需要129B,就给完整的2块,现在的一个难题是,用户使用时候越界访问以及释放的地址是无效的怎么处理?请给一些指点,谢谢。
[/Quote]

无碎片, lz 可以保证无内部碎片吗?
AnYidan 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

引用楼主 的回复:
需要写一个无碎片的malloc,大家给点意见吧,谢谢。


领导安排的工作,让我写一个公司内部自己使用的,初步想法是每块固定长度128B,小于128B就直接就给一个完整的块,比如需要129B,就给完整的2块,现在的一个难题是,用户使用时候越界访问以及释放的地址是无效的怎么处理?请给一些指点,谢谢。
[/Quote]

无碎片, lz 可以保证无内部碎片吗?
mymtom 2012-09-14
  • 打赏
  • 举报
回复
无碎片,这么可能?
whosyour_dady 2012-09-14
  • 打赏
  • 举报
回复
坐等高人出现
zhusizhi007 2012-09-14
  • 打赏
  • 举报
回复
这个有难度啊,做编译器的就是搞这些东东的,
aozhi 2012-09-14
  • 打赏
  • 举报
回复
你先说你总体的思路吧。
HULIHONG 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
需要写一个无碎片的malloc,大家给点意见吧,谢谢。
[/Quote]

领导安排的工作,让我写一个公司内部自己使用的,初步想法是每块固定长度128B,小于128B就直接就给一个完整的块,比如需要129B,就给完整的2块,现在的一个难题是,用户使用时候越界访问以及释放的地址是无效的怎么处理?请给一些指点,谢谢。
HULIHONG 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 的回复:]

引用 21 楼 的回复:

咱们平时所用的malloc及tcmalloc是通过什么算法将一大块内存分成若干种类的小块,比如32B 64B 256B等,这些种类的小块各自有多少个呢?是通过什么算法来实现的?谢谢。


概念错误。malloc并不只有所谓内存池,所谓内存池只是堆的前端,后端还是标准的堆算法。
[/Quote]

能给一个后端的标准算法链接吗?谢谢
cfvmario 2012-09-14
  • 打赏
  • 举报
回复
最近在借鉴一个树结构的内存分配器,实现起来发现问题好多。。原始代码是C++的封装的好,好写,但微观效率不行,我们要改成C用过程式代码写,写的工作量大多了还容易出bug。。
「已注销」 2012-09-14
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

咱们平时所用的malloc及tcmalloc是通过什么算法将一大块内存分成若干种类的小块,比如32B 64B 256B等,这些种类的小块各自有多少个呢?是通过什么算法来实现的?谢谢。
[/Quote]

概念错误。malloc并不只有所谓内存池,所谓内存池只是堆的前端,后端还是标准的堆算法。
chenbin0522 2012-09-14
  • 打赏
  • 举报
回复
nginx 内存池
加载更多回复(5)

70,023

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧