标签:
作为一个C/C++程序员,熟悉内存管理尤为重要,即使不使用C/C++语言,熟悉内存管理方面的知识也能够让你写出一些高效的代码。我们经常说的内存管理其实并不是直接对物理存储器进行管理,这个操作系统已经帮我们做了,我们讨论的是对虚拟存储器进行管理。
虚拟存储器实际上就是操作系统提供的一种对主存的抽象,以便更有效的管理存储器,它将主存看作磁盘上的地址空间的高速缓存,为每个进程提供了一致的地址空间,并保护进程的地址空间不被其他进程破坏。具体如何映射到实际的物理内存不在本文的讨论范围。
操作系统为每个进程提供的单独的地址空间,在32位linux系统上,进程的地址空间是4G,包括1G的内核地址空间,和3G的用户地址空间。
Linux进程的虚拟存储器
虚拟存储器中位于 0xc0000000 之上的部分为内核虚拟存储器,它包含内核的代码和数据,部分区域映射到所有进程共享的物理页面,其他区域包含每个进程不同的数据。从代码层面来说,我们只需要关注用户栈和堆。
用户栈是从地址空间的高地址向低地址增长,而堆则是从地址值向高地址增长。因此,一般不需要考虑栈空间与堆空间重叠问题。其中栈的大小可以通过ulimit –a查看,一般默认为10M,而堆大小理论上有3G,但是同时能够使用的真正最大内存只有swap空间+物理空间这么大。栈内存的分配是由操作系统进行管理,而堆内存则是程序管理。使用栈时需要注意栈溢出的问题即可,对堆的使用则是本文要讨论的重点,也是C/C++中内存管理的重点。
堆由动态存储器分配器进行管理,分配器有两种不同的风格,根据内存释放的方式分为显式分配器和隐身分配器(垃圾收集器)。显式分配器中已分配块内存的释放要求显式调用free或delete,而隐式分配器则会自动检测一个已分配块何时应该释放。
一个设计良好的内存分配器需要能最大化存储器的利用率,而造成利用率很低的主要原因就是内存碎片问题,当虽然有空闲内存,然而并不能满足分配请求时就是出现这个现象。碎片又分为内部碎片和外部碎片,内部碎片指的是已分配的内存比所需要的内存空间大,造成内存空闲,外部碎片指的是空闲块的内存合计起来能够满足分配要求,但是没有一个单独的空闲块能够满足要求。当大量分配/释放小块内存,就极有可能造成外部碎片。如何处理碎片问题自然也是设计分配器需要考虑到的问题了。
实现一个内存分配器需要必须要考虑到以下问题:1、 空闲块的组织 2、 放置 3、 分割 4、 合并
隐式空闲链表是将空闲块通过头部中的大小字段隐含的连接在一起的,通过遍历每个块,可以间接的访问空闲块,然而采用这种组织形式,空闲链表的搜索与堆中的以分配的块和空闲块总数显线性关系,优点就是处理起来很简单。其实更通用的还是采用显式空闲链表的形式。
显式空闲链表是将每个空闲块通过链表的形式组织起来,每个空闲块分配固定大小区域用来存放链表所需要的额外的信息。
实际上我们在实现分配器的过程中更多的是采用一种被称为“分离存储”的方法,对于单个空闲链表来说,块的大小都是固定的。因此,分配器维护一个空闲链表数据,每个链表块的大小根据具体的规则分配。
标签:
原文地址:http://www.cnblogs.com/mjammer/p/5028831.html