dlmalloc解析连载(4)

原文地址:http://blog.chinaunix.net/uid-7907749-id-2037210.html


上两篇讲解的chunk块是dlmalloc内比较细粒度的管理结构,比它们更大的内存块被称之为段(segment),其结构体以及相关定义如下:

struct malloc_segment {
  char*        base;             /* base address */
  size_t       size;             /* allocated size */
  struct malloc_segment* next;   /* ptr to next segment */
  flag_t       sflags;           /* mmap and extern flag */
};
#define is_mmapped_segment(S)  ((S)->sflags & IS_MMAPPED_BIT)
#define is_extern_segment(S)   ((S)->sflags & EXTERN_BIT)
typedef struct malloc_segment  msegment;
typedef struct malloc_segment* msegmentptr;

       英文注释很清晰,字段 base表示该 segment 段的起始地址,size表示该segment段的大小,sflags是一个标记字段(两个标记,一个用于标记该segment段是否为通过mmap申请,一个用于标记该segment段是否已经分配),而字段next用于指向下一个segment段,从而形成单链表结构。记录该单链表表头的变量同样定义在结构体malloc_state内,如下:

msegment seg;

       这是个结构体变量,而不是结构体指针变量,这一点需要注意。刚才已经提到多个segment段是通过next形成单链表结构来组织管理的,dlmalloc也没有对它们做其它特别的索引处理,因此查找某一个segment段需要在该单链表内线性扫描查找,不过大多数情况下,segment段应该是非常少的,所以并不会造成多大的性能损失。

 

       dlmalloc中对malloc_chunkmalloc_tree_chunkmalloc_segment给出了一个统一的管理结构体,那就是前面反复提到的结构体malloc_state,其具体定义如下:

struct malloc_state {
  binmap_t   smallmap;
  binmap_t   treemap;
  size_t     dvsize;
  size_t     topsize;
  char*      least_addr;
  mchunkptr  dv;
  mchunkptr  top;
  size_t     trim_check;
  size_t     magic;
  mchunkptr  smallbins[(NSMALLBINS+1)*2];
  tbinptr    treebins[NTREEBINS];
  size_t     footprint;
  size_t     max_footprint;
  flag_t     mflags;
#if USE_LOCKS
  MLOCK_T    mutex;     /* locate lock among fields that rarely change */
#endif /* USE_LOCKS */
  msegment   seg;
};
typedef struct malloc_state*    mstate;

       该结构体中的一些字段在前面已经详细分析过了,比如 smallbinstreebinsseg。其它几个字段的作用,现在也一一说明如下:

       smallmap是一个位图,用于标记对应的smallbins链表是否为空,1表示非空,0表示空。

       treemap也是一个位图,用于标记对应的treebins树是否为空,1表示非空,0表示空。

       dvdvsize指向一个chunk块(dvsize记录该chunk块大小),该chunk块具有一个特点就是:它是从最近的一次被用来服务小于256字节内存申请的chunk块中分裂出来的chunk块。比较拗口,简单点说就是为了更有效的利用程序的局部性原理,即对于应用程序的一连续小块内存申请请求,dlmalloc总是尽量从同一个chunk块获取空闲内存来满足这些请求,因此分配给应用程序的内存都是在挨得比较进的地址空间内,从局部性原理可以知道,这种内存分配方式在一定程度上可以提高应用程序的性能。当然,这种分配方式只是尽量,如果有其它更好的chunk块选择(比如刚好有某个chunk大小就是应用程序申请的内存大小)则会选择其它chunk块来分配内存,这在后面具体代码的分析过程中会看到。

top topsize也是指向一个chunk块(topsize记录该chunk块大小),该chunk块比较特殊,它位于当前活跃segment段(即最近被用来分配空间服务应用程序内存请求的)的最顶端,在最顶端的好处就是它可以自由伸缩(通过库函数sbrk()),因此大小可变。在segment段中间的那些chunk块大小一般不可变,但是有两种情况会变动,一是分配出去一部分内存用于满足应用程序申请,此时chunk块变小;二是,当应用程序释放内存(free())时,dlmalloc将检查该释放内存是否可与前后空闲内存合并,此时就可能导致chunk块变大。

字段least_addr用来记录可获取的最小内存地址,直白点说就是相当于一个边界点,用于做越界安全检查。

字段trim_check用来记录一个临界值。我们知道应用程序free内存空间时,该释放的内存空间并没有直接返还给系统而是被送回dlmalloc中进行管理。当释放的内存空间越来越多时,dlmalloc中管理的空闲chunk块也会变得越来越大(即由于多个连续空闲块合并的结果),对于一个segment段中间的空闲chunk块没有办法释放给系统(因为释放中间的空闲chunk块会将segment段切断),但是对于顶端(top)的chunk块则可以自由伸缩的,缩小顶端的chunk块也就是将空闲内存真正的返还给系统。那什么时候需要缩小顶端的chunk块呢?字段trim_check就是记录的这个临界值。当顶端的chunk块大小大于字段trim_check记录的值时就要进行缩减操作了,具体情况在后面源码分析时再看。

其它几个字段不是我们主要关注的内容且比较简单,比如字段magic用于做确认检查;字段mflags用于标记一些属性,比如启用mmap、禁用连续分配,……;字段footprint记录从系统获得的字节数目;字段max_footprint记录从系统获得的最大字节数目;字段mutex用于多线程互斥锁等等,在此就不做过多介绍了。

 

       dlmalloc定义了一个结构体malloc_state的全局变量,相关代码如下:

static struct malloc_state _gm_;
#define gm                 (&_gm_)
#define is_global(M)       ((M) == &_gm_)
#define is_initialized(M)  ((M)->top != 0)

   

       变量_gm_是结构体malloc_state的静态全局变量,因此当使用dlmalloc的应用程序被加载运行时,变量_gm_自动初始化为全0,这对于dlmalloc是十分重要的(例如_gm_结构体字段seg也为全0);另外还有一个gm宏,其值被定义为_gm_的地址,因此可以当指针一样使用它。其它两个宏is_globalis_initialized很明朗,无需多说。

 

       总结起来,可以看到dlmalloc利用静态全局变量_gm_管理着segment段,小块空闲chunk块、大块空闲chunk块以及其它相关信息,所有的内存分配和释放都是围绕着_gm_进行的。


dlmalloc是目前一个十分流行的内存分配器,其由Doug Lea(主页为http://gee.cs.oswego.edu/)从1987年开始编写,到目前为止,最新版本为2.8.3(可以从ftp://g.oswego.edu/pub/misc/malloc.c获取),由于其高效率等特点被广泛的使用(比如一些linux系统等用的就是dlmalloc或其变形,比如ptmalloc,主页为http://www.malloc.de/en/index.html)和研究(各位可以搜索关键字“GCspy”)。 dlmalloc的实现只有一个源文件(还有一个头文件),大概5000行,其内注释占了大量篇幅,由于有这么多注释存在的情况下,表面上看上去很容易懂,的确如此,在不追求细节的情况,对其大致思想的确很容易了解(没错,就只是了解而已),但是dlmalloc作为一个高品质的佳作,实现上使用了非常多的技巧,在实现细节上不花费一定的精力是没有办法深入理解其为什么这么做,这么做的好处在哪,只有当真正读懂后回味起来才发现它是如此美妙。 lenky0401个人博客将陆续推出对dlmalloc解析(针对Doug Lea Malloc的最新版Version 2.8.3,未做说明的情况下以32位平台,8字节对齐作为假定平台环境设置考虑),由于个人水平有限,因此也不能完全保证对dlmalloc的所有理解都准备无误, 但是所有内容均出自个人的理解而并非存心妄自揣测来愚人耳目,所以如果读者发现其中有什么错误,请勿见怪,如果可以则请来信告之,并欢迎来信讨论(lenky0401@163.com)。 这一系列文章是lenky0401在看完dlmalloc的大部分代码后的再总结,不能保证对dlmalloc的整体完全把握,贴出这些只是希望可以提前收到对此有研究的网友的指点,以便在最后对这一系列文章整理而形成的PDF文档中错误能少一些。至于对于现在贴出来的内容中包含的错误给大家造成的不便提前说声抱歉。:) 描述的内容不会包含dlmalloc全部代码,但会将这其中涉及到的一些技巧尽量讲出,我相信对dlmalloc源代码不感兴趣的朋友也可以学到这些独立的技巧而使用在自己的编程实践中。:) 最后,转载请保留本博客地址连接[http://lenky0401.cublog.cn],谢谢。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值