在没有其他技术支持下,分配给一个进程的内存空间肯定是连续的,如何分配内存给进程才能使得物理内存利用率更好?因为每个进程执行时间不同,所以会有一些进程比较早结束,也有一些进程开始得比较晚。当给新的进程分配内存空间时,有些释放掉的内存空间比较小,无法利用,自然就形成了内存碎片。

1.1 内存碎片

连续内存分配是指给进程分配一块不小于指定大小的连续物理内存。如图1所示,一开始先给3个进程分别分配了连续的物理内存,其中进程P1占地址空间0-2,进程P2占地址空间3-8,进程P3占地址空间9-14。然后进程P2被释放掉,此时若需要分配一个内存大小为10的进程,则进程2释放掉的内存空间无法给新进程,形成了内存碎片,可以看出所谓的内存碎片其实是相对而言的。

<center>图1</center>

内存碎片分类:

  • 外部碎片:已分配内存单元之间的未被使用的内存空间,例如进程P2所释放的内存
  • 内部碎片:分配单元内部的未被使用的内存,但是取决于分配单元是否需要取整。例如分配510内存空间,实际上只能分配512这种$2^n$大小的内存空间,这时候就有2个字节无法利用,形成内部碎片,如图2所示的黄色块。

<center>图 2</center>

1.2 连续内存分配-动态内存分配

连续的内存空间如何分配才能更好地提高内存利用率?根据进程指定一个大小可变的分区(块,内存块)称为动态内存分配,因为根据实际情况为进程找一块合适大小的内存块,大一点的进程自然也分配更大的内存块,小的进程分配小的内存块,所以就叫动态内存分配了。

现在假设有5个进程,占用内存情况如图3(左)所示,进程2和进程4结束后内存占用情况如图3(右)所示。

<center>图3</center>

因为要给新进程找个合适的内存块,首先得知道哪些内存块是空闲的,哪些是被配置,操作系统需要维护这样的一个数据结构。那又该如何给某个进程找到一个合适的内存块使得总体内存利用率更好呢,有几种策略。分别是最先匹配法,最佳匹配法还有最差匹配法。

最先匹配法:分配n个字节,使用第一个可用的比n大的空闲块。举个栗子,图4(左)蓝色块表示为按地址排列的空闲块,如果要分500B内存块给新进程 ,那么1K内存块会是第一个找到的比500B大的空闲块,将其中的500B内存分配给新进程,新的被分配的内存块会加入被分配的列表中,并注明是哪个进程所使用,剩下的500K空闲块更新到空闲列表中去。分配后空闲块如图4(b)所示。

<center>图4</center>

实现过程:

  • 空闲分区按地址排序
  • 分配过程中,从头往后找,寻找第一个合适的内存块,更新列表
  • 释放内存块时,检查是否可与邻近空闲块合并,更新列表

优点:

  • 简单
  • 因为前面找到了就不再往后找了,所以高地址可能会保留着较大的内存块,方便后面更大的进程申请更大的内存块。

缺点:

  • 因为会把较大内存块分成几个小块,所以会留下外部碎片
  • 因为留下外部碎片,所以申请较大内存块时候会比较慢

最佳匹配:分配n字节内存块时候,查找并使用大于n的最小内存块。举个栗子,还是如图4(左)所示,蓝色块表示为按地址排列的空闲块,如果要给新进程分配2k字节的内存块,那么将会找到最后那个3k内存块,并分配2k被新进程,同时并更新被分配和空间内存的列表。

实现过程如下所示:

  • 空闲分区从小到大排序
  • 分配时,查找一个合适的分区,更新列表
  • 释放内存块时,查找并且合并邻近的空闲区域(如果找到,邻近指的是地址邻近不是大小邻近,所以相比最先匹配复杂一些),更新列表

优点:

  • 可避免的空闲分区被拆分
  • 可减少外部碎片大小
  • 相对简单

缺点:

  • 释放内存块时较慢
  • 外部碎片
  • 容易产生很多无用的小碎片

最差匹配:分配n字节,使用尺寸不小于n的最大空闲内存块。举个栗子,还是如图4(左)所示,蓝色块表示为按地址排列的空闲块,如果要给新进程分配2k字节的内存块,那么将会找到中间那个10k内存块,并分配2k被新进程,同时并更新被分配和空间内存的列表。

实现过程如下所示:

  • 空闲分区按从大到小排序
  • 分配选最大内存块
  • 释放内存块时,检查是否可与邻近的空闲块去进行合并,并调整空闲内存块列表顺序

优点:

  • 中等大小的内存块分配较多时,效果最好,因为剩下那内存块还能利用起来,相当于剩下的小块比较少
  • 避免出现太多的碎片

缺点 :

  • 释放分区较慢
  • 外部碎片
  • 容易破坏大的空闲内存块,后续难以分配大的内存块# 3.物理内存管理-连续内存分配

WeifaGan
36 声望48 粉丝