malloc的内存用free释放后为何系统回收不了

要解释这个问题首先要了解Linux进程使用内存的基本流程,进程的堆并不是直接建立在Linux的内核的内存分配策略上的,而是建立在glibc的堆管理策略上的(也就是glibc的动态内存分配策略上),堆的管理是由glibc进行的。 所以我们调用free对malloc得到的内存进行释放的时候,并不是直接释放给操作系统,而是还给了glibc的堆管理实体,而glibc会在把实际的物理内存归还给系统的策略上做一些优化,以便优化用户任务的动态内存分配过程。

 

glibc维护了不止一个不定长的内存块链表,而是好几个,每一个这种链表负责一个大小范围,这种做法有效减少了分配大内存时的遍历开销,类似于哈希的方式,将很大的范围的数据散列到有限的几个小的范围内而不是所有数据都放在一起,虽然最终还是要在小的范围内查找,但是最起码省去了很多的开销,如果只有一个不定长链表那么就要全部遍历,如果分成3个,就省去了2/3的开销,总之这个策略十分类似于散列。glibc另外的策略就是不止维护一类空闲链表,而是另外再维护一个缓冲链表和一个高速缓冲链表,在分配的时候首先在高速缓存中查找,失败之后再在空闲链表查找,如果找到的内存块比较大,那么将切割之后的剩余内存块插入到缓存链表,如果空闲链表查找失败那么就往缓存链表中查找,这么查找有什么依据吗?实际上是有的,正是这个方式让glibc有了自己的策略。
 
 
在free的时候如果能合并在堆顶,也就是能和堆顶的空闲元素合并,那么就合并,因为堆的缩减仅仅在堆顶的空闲元素达到一定量的时候才会进行,因此为了尽快将内存归还操作系统,尽量优先考虑堆顶的释放,但是如果不能合并,比如它和堆顶根本就没有相邻,那么如果该释放的块大小小于80字节,那么就直接将之挂在高速缓存中,为了防止别的块和它合并所以并不更改使用位,这里可以看到,glibc实际上为小于80字节的小内存块维护了一个高速的内存池,如果有小块内存需求,直接从此池中拿走一个即可,只需要从高速缓存摘除之并不需要修改使用位,因为高速缓存中的元素的使用位均为1,这个高速缓存在有大内存块分配需求并且几个分配策略都失败的时候会被回收,回收进空闲链表的过程涉及到相邻块的合并,合并之后就有可能满足稍微大一些的内存分配需求,这里为何将界限定位为80个字节呢?实际上是一个经验值,那么介于80字节和128k字节之间的内存块在释放的时候要将使用位设置为0,然后试图和相邻块合并,然后挂入缓存链表。
 

 

还有一个策略就是堆顶的特殊处理,堆顶不放在任何一个链表中,对它进行照顾就是因为为了更有效的将内存退还操作系统,因为堆的压缩只能从堆顶开始,操作系统只知道给了一个进程虚拟内存连续的一大块叫做堆的内存,别的什么也不知道,应用程序归还的时候同样需要连续的从堆顶归还而不能仅仅归还系统,归根结底要对堆顶进行特殊的处理。
 
原文:
http://hi.baidu.com/hiei1125/blog/item/56d6d5a29322c2a3caefd023.html
http://blog.loudly.me/2010/02/glibc_malloc_memory_overhead/
http://www.sooset.com/q_90caa1d6daf6918c8017804fe5df0dc7.html

Tags: malloc

上一篇: Cannot assign requested address   下一篇: jq select input常见操作

提交疑问

回顶部