首页 > C > 内存 阅读:57,774

内存分配优化:碎片,速度与多线程支持,tcmalloc

< 上一页 malloc/calloc/realloc 内存池 下一页 >

内存碎片一般是由于空闲的连续空间比要申请的空间小,导致这些小内存块不能被利用,而形成内存碎片。

 

调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。

 

多次调用malloc()后空闲内存被切成很多的小内存片段,这就使得用户在申请内存使用时,由于找不到足够大的内存空间,malloc()需要进行内存整理,使得函数的性能越来越低。聪明的程序员通过总是分配大小为2的幂的内存块,而最大限度地降低潜在的malloc性能丧失。也就是说,所分配的内存块大小为4字节、8字节、16字节等。分配小而且生命周期很长的内存块时,才容易导致内存碎片的出现。

 

所以如果要动态分配的空间比较小,一般采取先分配一大块空间。然后在有内存分配需求时,从大块空间依次取出。

如果分配的空间很快就会释放(如分配释放同时在一个函数内),那么就不需要考虑内存碎片问题。

 

要避免内存分配中的碎片问题,一是尽量避免对malloc()函数的调用,能在栈上分配解决问题的,就不用malloc()来解决。

而在不得已非要频繁调用malloc()函数来分配内存的时候,就需要考虑内存池的技术了。内存池是池化技术中的一种,还包括连接池,线程池等。连接池用来解决频繁连接的问题,线程池用来解决频繁线程创建的问题,而内存池就是用来解决频繁内存分配产生内存碎片的问题。比如一开始直接用malloc申请一大段内存,做成一个内存池,然后再定义一组分配和释放接口对其进行管理,避免了频繁申请和释放内存。

 

tcmalloc:TCMalloc (google-perftools) 是用于优化C++写的多线程应用,比glibc 2.3malloc快。这个模块可以用来让MySQL在高并发下内存占用更加稳定

 

tcmalloc就是一个内存分配器,管理堆内存,主要影响mallocfree,用于降低频繁分配、释放内存造成的性能损耗,并且有效地控制内存碎片。glibc中的内存分配器是ptmalloc2tcmalloc号称要比它快。一次mallocfree操作,ptmalloc需要300ns,而tcmalloc只要50ns。同时tcmalloc也优化了小对象的存储,需要更少的空间。tcmalloc特别对多线程做了优化,对于小对象的分配基本上是不存在锁竞争,而大对象使用了细粒度、高效的自旋锁(spinlock)。分配给线程的本地缓存,在长时间空闲的情况下会被回收,供其他线程使用,这样提高了在多线程情况下的内存利用率,不会浪费内存,而这一点ptmalloc2是做不到的。

源码不需任何修改,tcmalloc会自动替换掉glibc默认的mallocfree

g++ test.cpp -ltcmalloc  LINUX平台tcmalloc: Thread cached malloc

< 上一页 malloc/calloc/realloc 内存池 下一页 >

周哥教IT,分享编程知识,提高编程技能,程序员的充电站。跟着周哥一起学习,每天都有进步。

通俗易懂,深入浅出,一篇文章只讲一个知识点。

当你决定关注「周哥教IT」,你已然超越了90%的程序员!

IT黄埔-周哥教IT技术交流QQ群:213774841,期待您的加入!

二维码
微信扫描二维码关注