本文转自:http://blog.csdn.net/dog250/article/details/5303284

linux的内核是c语言写成的,而且它的内存本质上也是由c语言写成的代码回收的,这里不考虑用户空间的标准c库的delete方式回收,毕竟delete释放了内存后最终还是要由sys_brk(linux下)通过内核来回收的,而且,无论是malloc还是delele操作,操作的都是虚拟内存,但是inux管理的是物理内存,虚拟内存是操作系统固有的机制,在32位的系统上就是4G,因此,linux内存管理的精髓就是物理内存的管理,因此,我这里要说的就是linux的物理内存回收管理机制。谈到物理内存回收,主要涉及到两个方面,一个是用户空间的物理内存回收,一个是内核空间的物理内存回收,这里要注意的是用户空间物理内存虽然回收了,但是其虚拟内存可能仍然没有被delete,这里的内核的物理内存管理和用户glibc库的内存管理是两码事。linux的用户内存管理的精髓就是页式管理,也就是说对于用户内存,是一页一页进行管理的,如果用户进程访问的虚拟内存还没有映射到物理内存,那么缺页中断处理会将页面调入,记住,只会调入一个页面,而不会调入更多的页面,不管用户的数据在虚拟内存占用几个页面,它最终只会在访问到一个地址的时候才有可能缺页,而一个地址不会占用超过一个页面的,linux的这种懒惰的方式促使用户页面的管理非常简单,就是以页面为单位的管理,用户进程的所有页面都会加入到lru链表中,内核每隔一段时间就会对lru链表进行扫描,然后回收那些没有可以被回收的物理内存,这里的要点就是,lru是固定为页面大小的内存连接而成的链表,这样的话就不会牵扯变长问题,所有的内存大小都是一样的,因此就比较好管理,内核的用户空间物理内存回收系统只需要统一的进行页面级别的扫描并回收就可以了,其实就是对lru链表进行扫面,而lru链表的对象就是单个页面,这是一种非常统一的方式,效率非常高。操作系统的请求调页机制使得每次申请的就是一个页面而不是一个数据结构,内核对内面管理是很容易的,但是却不易管理复杂的可变的用户数据结构,因此内核的用户空间的物理内存回收机制才可以如此高效和简单,简单说来就是利用基于页面的引用计数机制配合页面老化机制来加以回收,记住这里的回收不会废掉页面上的数据,而只会将数据暂时转移到一个后备空间中,因此回收不必考虑太多用户数据刷新的问题。页面老化机制其实就是一个基于效率的强化机制,它并不涉及可靠性和正确性问题,而仅仅是效率问题。

既然linux对用户空间的内存是如此对待的,那么内核空间呢?linux的内核空间内存是不可换出的,也就是说如果一个内核数据结构占据了一块物理内存,那么在此物理内存释放之前,这块物理内存将一直被它占有,这是linux内核空间物理内存管理的前提,在这个前提下,也就出现了内核独有的内存回收方式,一种和用户空间物理内存回收方式截然相反的回收方式,内核的内存回收不是基于固定大小的页面的,而是基于可变大小的数据结构的,其实正是因为内核数据结构的不可换出从而消除了换入换出导致的混乱性才使得基于数据结构的内存回收成为可能,在内核空间,所有的已分配内存不被加入到lru链表,也就是说它们不受基于页面的内存回收机制的管理,相反的,它们有自己的一套管理方式,其中包括slab的方式,vmalloc的方式,还有直接嫁接于伙伴系统之上的高端内存映射的方式,slab直接从伙伴系统得到内存,然后由slab系统将之奉还,也就是说伙伴系统没有权利主动要回分配给slab的内存,毕竟它们同样服务于内核,地位相同。然而slab本身却知道自己申请了多少内存,每个对象有多大,当slab不再用这些内存的时候,slab系统会自己将整个slab内存归还伙伴系统,在slab归还之前,伙伴系统没有任何权利自己索要内存。slab归还的内存的大小由该slab所代表的数据结构决定。

总结一下,linux内核对于用户空间的内存是按照页面以懒惰方式进行回收的,但是并非内存全部用完才回收,而是基于以下原则:1.定时回收。这样可以保证可用内存数量的稳定性;2.设置一个阀值,在可用内存数量低于这个值得时候进行回收,直到高于一个值,这样可以保证可用内存永远在一个合适的范围内。linux对用户空间内存的这种策略是为了在实现复杂度和效率以及稳定性之间作一个权衡。Linux对内核的内存回收时积极方式进行的,因为内核内存的不可换出性从而导致内核内存一旦回收就是真正的回收,因此懒惰方式回收是最有效的方式。