内存碎片产生原因

内存分配有静态分配和动态分配两种,动态分配将不可避免会产生内存碎片的问题。内存碎片即“碎片的内存”描述一个系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用。

内存碎片分为:内部碎片和外部碎片。

外部碎片就是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间;

内部碎片是处于区域内部或页面内部的存储块。占有这些区域或页面的进程并不使用这个存储块。而在进程占有这块存储块时,系统无法利用它。直到进程释放它,或进程结束时,系统才有可能利用这个存储块。

单道连续分配只有内部碎片。多道固定连续分配既有内部碎片,又有外部碎片。

外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。

外部碎片是出于任何已分配区域或页面外部的空闲存储块。这些存储块的总和可以满足当前申请的长度要求,但是由于它们的地址不连续或其他原因,使得系统无法满足当前申请。

内存碎片解决办法

  1. 不要动态分配内存,尽量使用栈。
  2. 根据使用场景定制内存分配策略。比如如果你发现你常常分配同一种大小的数据结构,那么就一次分配一个数组从里面挨个取,用完了扔回旁视列表里以备重用。再如你发现内存分配具有周期性,那么你可以周期性的分配大块内存,用完了释放整块内存。
  3. 不手动管理内存,改用具有分代内存管理、增量垃圾收集和内存整理功能的垃圾收集器。比如你要是用了Java,那么你就再也不用操心内存碎片了(代价是你的程序占用的内存会比用C语言时翻几倍)。

伙伴系统算法:基于索引搜索的动态分区分配算法,已分配分区和空闲分区大小都是2的k次方,当需要分配长度为n的存储空间时,从n开始找最接近2的i次方大小的分区,若没有就不断向上找分割。

页式存储

内存管理方式主要分为:页式管理、段式管理和段页式管理。

页式管理:将用户程序的地址空间分为若干个固定大小的区域,称为页,典型页面大小为1kb。将内存空间分为若干个物理块或页框,页和快大小相等。将用户程序的任一也放入任一物理块中,实现离散分配。

优点:没有外碎片,每个内碎片不超过页的大小。

缺点:程序全部装入内存,要求有相应的硬件支持,如地址变换机构缺页中断的产生和选择淘汰页面等都要求有相应的硬件支持。增加了机器成本和系统开销。

段式管理的基本思想是把用户程序的地址空间分成大小不同的段,每段可定义一组相对完整的信息。段式管理程序以段为单位分配内存,然后通过地址映射机构把段式虚拟地址转换为实际内存物理地址。

优点:可以分别编写和编译,可以针对不同类型的段采取不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。

缺点:会产生碎片。

段页式管理,系统必须为每个作业或者进程建立一张段表以管理内存分配与释放、缺段处理等。另外由于一个段又被划分为若干个页,每个段必须建立一张页表以把段中的虚页变换为内存中的实际页面。显然与页式管理时相同,页表也要有相应的实现缺页中断处理和页面保护等功能的表项。

段页式管理是段式管理和页式管理相结合而成,具有两者的优点。

由于管理软件的增加,复杂性和开销也增加。另外需要的硬件以及占用的内存也有所增加,使得执行速度下降。