注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

中吴南顾惟一笑

成功法则就是那19个字

 
 
 

日志

 
 

关于内存管理——分配与回收  

2010-09-20 19:21:22|  分类: R&D |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
malloc和free是两个核心函数,而calloc和realloc之所以存在,完全是为了提高效率的缘故,可以用malloc和free的组合来模拟它们。

拿calloc函数的实现来说,在32位机上,内存管理器保证内存至少是4字节对齐的,其长度也会扩展到能被4字节整除,那么其清零算法就可以优化。可以一次清零4个字节,这大大提高清零速度。

拿realloc函数的实现来说,如果realloc的指针后面有足够的空间,内存管理器可以直接扩展其大小,而无须拷贝原有内容。当然,新大小比原来还小时,更不拷贝了。相反,通过malloc和free来实现realloc时,两种情况下都要拷贝,效率自然会低不少。

另外还有一个非标准的函数也涉及到内存分配:strdup。在linux和win32下都支持,非常方便。也可以用malloc来模拟,而且没有性能上的损失。

在设计内存管理算法时,要考虑什么因素?

兼容性

内存管理接口函数要遵循现有标准(如POSIX或者Win32),让使用者可以平滑的过度到新的内存管理器上。

可移植性

通常情况下,内存管理要向OS申请内存,然后进行二次分配。所以,在适当的时候要扩展内存或释放多余的内存,尽量抽象出平台相关的代码,保证可移植性。

减少空间浪费

内存管理必然要使用一些自己的数据结构来存储信息,这些数据结构本身也要占内存空间。太多的话显然是不可以接受的。

内存碎片也是浪费空间的罪魁祸首,若内存管理器中有大量的内存碎片,它们是一些不连续的小块内存,它们总量可能很大却无法使用,这也是不可以接受的。

速度

内存分配/释放是常用操作。按照20/80法则,常用的操作的性能对系统的整体性能尤为重要。


可调性(以适应于不同的情况)

内存管理算法设计的难点就在于要适应用不同的情况。事实上,如果缺乏应用的上下文,是无法评估内存管理算法的好坏的。可以说在任何情况下,专用算法都比通用算法在时/空性能上的表现更优。

为每种情况都写一套内存管理算法,显然是不太合适的。我们不需要追求最优算法,那样代价太高,能达到次优就行了。设计一套通用内存管理算法,通过一些参数对它进行配置,可以让它在特定情况也有相当出色的表现,这就是可调性。

局部性(Locality)

大家都知道,使用cache可以提高程度的速度,但很多人未必知道cache使程序速度提高的真正原因。拿CPU内部的cache和RAM的访问速度相比,速度可能相差一个数量级。两者的速度上的差异固然重要,但这并不是提高速度的充分条件,只是必要条件。

另外一个条件是程序访问内存的局部性(Locality)。大多数情况下,程序总访问一块内存附近的内存,把附近的内存先加入到cache中,下次访问cache中的数据,速度就会提高。否则只会使数据在内存与cache之间来回搬运,不但于提高速度无益,反而会大大降低程序的速度。因此,内存管理算法要考虑这一因素,减少cache miss和page fault。

调试功能

作为一个C/C++程序员,内存错误可以说是我们的噩梦,上一次的内存错误一定还让你记忆犹新。特别对于嵌入式环境来说,内存错误检测工具缺乏,内存管理提供的调试功能就更是不可或缺了。

适应性


前面说了可调性,以便让内存管理器适用于不同的情况。但是,对于不同情况都要去调设置,无疑太麻烦,是非用户友好的。要尽量让内存管理器适用于很广的情况,只有极少情况下才去调设置。

设计是一个多目标优化的过程,有些目标之间存在着竞争。如何平衡这些竞争力是设计的难点之一。在不同的情况下,这些目标的重要性又不一样,所以根本不存在一个最好的内存分配算法。


Misc
1). 空指针和零长度内存

    free(NULL)会让程序crash吗?答案是不会,标准C要求free接受空指针,然后什么也不做。

    malloc(0)会分配成功吗?答案是会的,它会返回一块最小内存给你。



2). 对齐与取整

    内存管理器会保证分配出来的内存地址是对齐的,通常是4或8字节对齐。

    内存管理器会对要求内存长度取整,让内存长度能被4或8的整除。


3). glibc中的内存分配管理 — 如何知道free时内存的大小

第二个size_t指明自己的大小,同时还指明:自己是不是用mmap分配的(M),前面是否有一个效内存块(P)。你可能觉得奇怪,在32位机上,sizeof(size_t)就是32位,怎么还能留下两个位来保存标志呢?因为会对内存长度取整,保证最低2或3bits为0,即是空闲的。

   An allocated chunk looks like this:

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if allocated            | |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk, in bytes                       |M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             User data starts here...                          .
            .                                                               .
            .             (malloc_usable_size() bytes)                      .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk                                     |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


这两位标志相关定义为:
#define PREV_INUSE 0x1
#define IS_MMAPPED 0x2

由于malloc实现中是8字节对齐的,size的低3位总是不会被使用的,所以在实际计算chunk大小时,要去掉标志位.例如:
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
  评论这张
 
阅读(308)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017