dlmalloc(二)
来源:互联网 发布:python绝技pdf 百度云 编辑:程序博客网 时间:2024/06/06 09:48
dlmalloc中,申请到的内存被分割成若干个内存块,dlmalloc采用两种不同的数据结构表示这些内存块。小内存块保存在链表中,用struct malloc_chunk表示;大内存块保存在树形结构中,用struct malloc_tree_chunk表示。struct malloc_chunk结构如下:
struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk;};
fd表示链表中后面一个malloc_chunk结构,bk表示链表中前一个malloc_chunk结构。head表示这个malloc_chunk代表内存块的大小,另外还包含了一些标志信息。prev_foot表示前一个malloc_chunk的大小,这里的"前一个"不是链表中的"前一个",而是与这个malloc_chunk地址相邻的"前一个"。通过prev_foot和size两个字段dlmalloc就可以快速找到地址相邻的前一个和后一个malloc_chunk结构。
当内存块被分配给应用程序后,就会被从链表中摘除,这时malloc_chunk结构中的fd和bk两个字段就没有意义了,因此可以供应用程序使用。我们调用malloc()申请内存时,malloc()会返回一个指针,指向申请到的内存块的起始地址p,其实这个地址前还有一个malloc_chunk结构,我们可以通过p-8得到malloc_chunk结构的指针。反过来也可以通过malloc_chunk指针得到分配给应用程序的内存块的起始地址。为此dlmalloc定义了两个宏:
typedef struct malloc_chunk* mchunkptr;// 32位Linux系统中,TWO_SIZE_T_SIZES的值是8#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES))#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
我们看下面这个例子:
上面这块内存区域中包括两个内存块,分别为chunk1和chunk2,紧接着malloc_chunk结构的就是供应用程序使用的内存。按照前面的分析,fd和bk两个字段也可以供应用程序使用。因此ptr1 = chunk1 + 8,ptr2 = chunk2 + 8。还有一点需要注意的是,只有当前面一个chunk空闲时malloc_chunk结构中的prev_foot才保存前一个chunk的大小;当前面一个chunk分配给应用程序后,prev_foot字段也可以供应用程序使用。上图中,当chunk1分配给应用程序后,chunk2中的prev_foot字段就没有意义了,可以供应用程序使用。dlmalloc返回给应用程序的地址是ptr1,这个内存块的大小是size1 + 8 + 4。因此,malloc_chunk结构中,只有head字段永远不会挪作他用,其他三个字段都可以供应用程序使用,通过这种复用最大限度地减少了dlmalloc本身占用的内存。
dlmalloc对应用程序申请的内存长度有限制,要求内存块长度(包括malloc_chunk结构占用的内存)必须是8字节的倍数。假如应用程序调用malloc(13)申请长度为13字节的内存块,dlmalloc最终分配内存块大小是24字节,除去malloc_chunk结构中head占用的4字节,分配给应用程序的内存块大小是20字节。当然,应用程序不要揣测内存块的实际大小,虽然dlmalloc分配了20字节,但是应用程序最好只使用13字节,不要使用剩余的7字节。否则有两方面后果:(1)应用程序显得混乱,其他人可能无法读懂你的代码。(2)返回多少字节与内存分配器的实现方式有关,换另外一种内存分配器可能返回的就不是20字节了,如果应用程序使用超过13个字节就可能覆盖其他数据了,程序移植性差。
malloc_chunk结构可以表示的最小内存块是16字节,最大内存块是248字节,因此malloc_chunk可以表示16、24、32、40、......、248共30种长度的内存块。dlmalloc定义了30条链表,相同长度的空闲内存块保存在一个链表中。
超过248字节的内存就属于大块内存了,大块内存用malloc_tree_chunk表示,这个数据结构定义如下:
struct malloc_tree_chunk { /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; struct malloc_tree_chunk* fd; struct malloc_tree_chunk* bk; struct malloc_tree_chunk* child[2]; struct malloc_tree_chunk* parent; bindex_t index;};
其中prev_foot和head的定义跟malloc_chunk中的定义完全相同。那么其他几个字段表示什么含义呢?dlmalloc中小内存块只有30种情况,可以用30条链表存储;但是大内存块有无数种情况(256、264、272、......),因此就不能用链表表示了,大内存块保存在树形结构中,dlmalloc定义了32棵树存储大内存块,每棵树中存储若干种长度的内存块,每棵树保存的内存块范围如下:
dlmalloc中根据内存块大小计算所在树的编号的宏如下:
#define compute_tree_index(S, I)\{\ size_t X = S >> TREEBIN_SHIFT; /* TREEBIN_SHIFT的值是8 */ \ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1; /* NTREEBINS的值是32 */ \ else {\ unsigned int K;\ __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\}
如果感兴趣可以采用这个宏计算一下。我们看一下单棵树中保存的空闲内存块,以编号为0的树为例,这棵树中内存块的范围是[256, 384),按照前面规定内存块的大小必须是8的倍数,因此这棵树中保存的内存块长度分别为256, 264, 272, 280, 288, 296, 304, 312, 320, 328, 336, 344, 352, 360, 368, 376,共16种长度,每种长度的内存块作为树中一个节点。这棵树中可能保存了多个相同长度的内存块,这些相同长度的内存块构成了一棵链表,如下图所示:
现在回过头来看malloc_tree_chunk中各个字段的含义。
prev_foot表示前一个内存块的大小
head表示本内存块的大小
child表示两个子节点
parent表示父节点
index表示内存块所在树的索引号
fd表示链表中下一个内存块
bk表示链表中前面一个内存块
同样,这个结构中只有head字段保持不变,其他字段都可以供应用程序使用。
现在我们来看一个全局变量_gm_,这是struct malloc_state类型的变量,这个数据结构定义如下:
struct malloc_state { binmap_t smallmap; mchunkptr smallbins[(NSMALLBINS+1)*2]; binmap_t treemap; tbinptr treebins[NTREEBINS]; mchunkptr dv; size_t dvsize; mchunkptr top; size_t topsize; char* least_addr; size_t trim_check; size_t magic; size_t footprint;#if USE_MAX_ALLOWED_FOOTPRINT size_t max_allowed_footprint;#endif size_t max_footprint; flag_t mflags;#if USE_LOCKS MLOCK_T mutex;#endif /* USE_LOCKS */ msegment seg;};static struct malloc_state _gm_;我们重点关注前8个字段。smallbins就是dlmalloc中定义的30条链表(加上长度为0和8的内存块,共32条链表)。smalbins[0]-smallbins[3]共16字节,表示一个malloc_chunk结构,对应长度为0的链表。smalbins[2]-smallbins[5]共16字节,表示一个malloc_chunk结构,对应长度为8的链表,以此类推。可以看到相邻两个malloc_chunk结构有重合,这是因为作为链表使用时,malloc_chunk结构中的prev_foot和head字段没有意义,因此可以重合使用。smallmap是smallbins的位图,某个比特置位表示对应的链表上有空闲内存块,比特清零表示对应的链表为空。treebins表示dlmalloc中32棵树,treemap是treebins的位图,置位表示对应树中有空闲内存块,清零表示对应树为空。dv是一个特殊的内存块,如果dlmalloc中找不到一个合适大小的内存块分配给应用程序,那么dlmalloc会将一个较大的内存块分割成两个较小的内存块,一块给应用程序使用,另外一块保存在dv中。下载再找不到合适大小的内存块时,如果dv大小大于应用程序请求的内存块,dlmalloc会将dv分割成两块,一块给应用程序,另一块仍保存在dv中;如果dv小于应用程序请求的内存块,dlmalloc首先将dv保存在链表或树中,然后挑选另外一个内存块分割,一块给应用程序,另一块保存在dv中。因此dlmalloc分配内存块的原则是先匹配大小,后匹配位置,尽量挑选合适大小的内存块给应用程序,实在找不到合适的内存块时就尽量从同一个位置分割内存块,以提高效率(程序执行的局部性原理)。dvsize就是dv表示内存块的大小。top是另外一个特殊的内存块,表示堆空间中对顶端的内存块。dlmalloc尽量不使用这个内存块,只有在_gm_中没有合适大小的内存块并且没有更大的内存块可供分割时才使用top中的内存。为什么尽量不要使用top呢?因为当top被占用时dlmalloc没办法释放其他空闲内存,dlmalloc收缩堆时必须从高地址向低地址收缩,所以主要高地址的内存被占用,即使堆中有再多的空闲内存也没办法释放。topsize表示top的大小。
先说到这里,下篇文章中详细讲解malloc()申请内存的流程。
- dlmalloc(二)
- dlmalloc解析连载二
- dlmalloc(一)
- dlmalloc(三)
- dlmalloc(四)
- dlmalloc
- dlmalloc、nedmalloc
- dlmalloc源码
- dlmalloc 简析
- dlmalloc解析连载一
- dlmalloc解析连载三
- dlmalloc解析连载四
- dlmalloc解析连载一
- dlmalloc解析连载三
- dlmalloc解析连载四
- DLmalloc 内存分配算法
- dlmalloc解析连载 (1)
- dlmalloc解析连载(2)
- 面试题20131024
- 光电系统中的视频处理技术
- [Urgent]Technical Support Engineer IV - 2013/10/16
- .net加密
- 北上广深等全国一流机场航显信息系统现状-航显系统研究第90篇
- dlmalloc(二)
- Android的Activity的launchMode与onActivityResult方法的关系
- gdb core dumped
- 苹果证书常出现的问题
- NYOJ - 水池数目(DFS)
- @PathVariable和@RequestParam的区别
- 归档数据的读写方法。。。
- TCP的数据发送和接收
- java--内省