内存管理(二)——连续分配管理方式

简介: 内存管理(二)——连续分配管理方式

一、概要

在上节中我们已近介绍了,操作系统在内存管理中发挥的两个功能:地址转化、存储保护这节我们着重探讨:内存空间的分配和回收。不妨再来回顾下操作系统在内存管理方面提供了那些功能。

基本概念: 操作系统对内存的划分和动态管理。

带来的好处: 方便用户实用存储器、提高内存利用率、通过虚拟技术从逻辑上扩充内存。

OS提供的功能: ①内存空间的分配和回收 ②地址转化 ③内存扩充 ④存储保护

二、知识扩充

(1)内部碎片、外部碎片?

内部碎片: 已经分配给作业但不能被利用的内存空间
外部碎片: 系统中还没有分配给作业,但是由于碎片太小而无法分配给申请内存空间的新进程的存储块。
通俗讲: 某个作业所占的内存区域如果没有装满,就是内部碎片,而作业与作业之间,如果有内存区域没有分配给某个作业,但是又不能分配给任何作业,就是外部碎片。

(2)交换、覆盖技术?

①覆盖(Overlay):

历史: 早期的计算机系统,内存空间特别小,有时即使只存放一个作业,但是仍然不能一次全部装入内存。对此,我们引入了覆盖技术。
在这里插入图片描述

细说: 由于程序运行时候并非任何时候都要访问程序及数据的各个部分(尤其是大程序),因此可把用户空间分成一个固定区若干覆盖区。常用部分放在固定区(输入后就不在调出,除非运行结束)、不常用功能,在其他程序模块中实现,平时存放在外存中,在需要用到时装入内存。

例子:

A()//8KB
{
   
   
    B();//8KB
    C();//10KB
}
B()//8KB
{
   
   
    D();//12KB
}
C()//10KB
{
   
   
    E();//4KB
    F();//10KB
}

在这里插入图片描述
特点: 打破了必须将一个进程全部信息装入内存后才能运行的限制,但是当同时运行程序的代码量大于主存时仍不能运行,此外,内存中能够更新的地方只有覆盖区的段,不在覆盖区中的段会常驻内存。必须由程序员声明覆盖结构,操作系统完成自动覆盖。缺点:对用户不透明,增加了用户编程负担。覆盖技术只用于早期的操作系统中,现在已成为历史。

②交换(Swapping):

思想: 内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)、我们熟知的中级调度就是用的这种技术。
换出、换入: 把处于等待状态(或在CPU调度原则下被剥夺运行权利)的程序从内存移到辅存,把内存空间腾出来。把准备好竞争CPU运行的程序从辅存移到内存,这一个过程又称换入。
注意:
在这里插入图片描述

例子: 一个CPU 采用时间片轮转调度算法的多道程序环境。某一进程时间片到,内存管理器将这一进程换出,将另外一个进程换入刚刚释放的内存空间。同时CPU调度器可以将时间片分配给其他已经在内存中的进程。每个进程用完时间片都与另外一进程交换。理想情况下,内存管理器的交换速度够快,总有进程在内存中可以执行。

③两者比较:

交换技术主要在不同进程之间,而覆盖是同一进程之中。由于覆盖技术要求给出程序段之间的覆盖结构,使得其对用户和程序员不透明,所以对于主存无法存放用户程序的矛盾。现代操作系统是通过虚拟内存技术 来解决的,覆盖技术已经成为了历史;而交换在现代操作系统中任具有较强的生命力。

三、功能三:内存空间的分配和回收——(连续分配管理)

由操作系统完成内存空间的分配和管理,使得程序设计人员摆脱存储空间分配的麻烦,提高编程效率,为此,系统应该记住内存空间的使用情况:实施内存的分配;回收系统或用户释放的内存空间。
连续分配管理顾名思义:为用户分配一块连续的空间。主要有3种方式:①单一连续分配 ②固定分区分配 ③动态分区分配

(1)单一连续分配:

在单一连续分配方式中,内存被分为系统区用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区用于存放用户进程相关数据。内存中只能有一道用户程序,用户程序独占整个用户区空间,自然也不需要操作系统提供存储保护。(直接把用户区给整个用户程序)
在这里插入图片描述

优点:实现简单;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护(eg:早期的 PC 操作系统 MS-DOS)。
缺点: 只能用于单用户、单任务的操作系统中;有内部碎片;存储器利用率极低

(2)固定分区分配:

简述: 20世纪60年代出现了支持多道程序的系统,为了能在内存中装入多道程序,且这些程序之间又不会相互干扰,于是将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业,这样就形成了最早的、最简单的一种可运行多道程序的内存管理方式。当内存当有空闲区时候,便可从外存的后备作业队列中,选择合适大小的作业装入该分区,如此循环。一般采用静态重定位装入内存。

分区说明表: 且为了实现固定分区分配,OS需要建立一个数据结构:分区说明表,来实现各个分区的分配与回收。每个表项对应一个分区,通常按分区大小排列。每个表项包括对应分区的大小、起始地址、状态(是否已分配)。当某用户程序要装入内存时,由操作系统内核程序根据用户程序大小检索该表,从中找到一个能满足大小的、未分配的分区,将之分配给该程序,然后修改状态为“已分配”。
在这里插入图片描述

分区相等: 缺乏灵活性,程序太大时候,一个分区又不足以装入该程序,导致程序无法运行。
分区不等: 可以把内存划分为:多数较小的分区,适量中等大小分区,少数大分区。
在这里插入图片描述
优点: a.多道程序系统最简单的存储分配。b.实现简单,无外部碎片。
缺点: a. 当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能;b.程序小于固定空间大小 会产生内部碎片,内存利用率低。
应用: 不能实现多进程共享一个主存区,所以存储空间利用率低。适用于:某些控制多个相同对象的控制系统。(共享是指多进程都使用同一个主存段)

(3)动态分区分配:

简述: 这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。

数据结构:

为了实现动态分区分配,系统中也必须设置相应的数据结构来记录内存的使用情况。常用的数据结构:
1)空闲分区表:每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息。
2)空闲分区链: 用链头指针将内存中的空间分区链接起来,构成空闲分区链。实现办法:每个空闲分区的若干字节用来存放控制信息(前后指针、分区大小等)。

在这里插入图片描述

分区分配过程:

在这里插入图片描述
根据上面过程我们发现:动态分区在最开始的时候很好,但是随着时间的推移,内存中会产生越来越多的外部碎片,导致内存利用率随之下降,克服外部碎片,我们可以使用紧凑(Compaction) 技术,但是这需要硬件支持,相对耗时。(紧凑技术相当于Windows系统中的磁盘整理程序,只不过后者是针对外存空间的紧凑。)所以决定内存利用率的关键在于:分配算法

分区分配算法:

参考cm_cyj_1116的博客
将一个作业存入内存,应按照一定的分配算法从空闲分区表中选。如果选取的空闲分区的容量比作业空间大,那么将该空闲分区的一部分给作业,剩下的部分仍然留在空闲分区中,同时需要修改空闲分区表中的有关信息。那么如何从空闲分区中选择呢?

1)首次适应算法(First Fit):
空闲分区按照地址递增的次序用链表串成一个队列,每次需要为一个进程分配内存时都从队首开始找。顺着链队列(链式存储的队列),找到大小能满足要求的第一个空闲分区
在这里插入图片描述
优点: 优先利用存储低地址部分的空闲分区,从而保留高地址部分的大的空闲分区。
缺点: a.由于低地址部分不断划分,致使低地址端留下许多难以利用的很小的空闲分区(外部碎片)
b.每次查找又都是从低地址部分开始的(顺序访问),无疑增加了“查找可用空间”的开销。

2)临近适应算法(Next Fit):
算法思想: 该算法别名:循环首次适应算法,即在首次适应算法的基础上把队列改成循环队列,而不是每次从队首开始找空闲分区,而是从上次找到的空闲分区的下一个分区开始找。
如何实现: 空闲分区以地址递增的顺序排列(可排成一个循环链表)。每次分配内存时从上次查
找结束的位置开始查找空闲分区链,找到大小能满足要求的第一个空闲分区。
优点: 使得空闲空间分配更均匀,减少了查找空闲分区的开销。
缺点: 导致缺乏大的空闲分区。
在这里插入图片描述

3)最佳适应算法(Best Fit):
算法思想: 由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区,即,优先使用更小的空闲区。
算法实现: 空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区
在这里插入图片描述
优点: 总能分配给作业最适合的分区,并且保留大的分区。
缺点: 虽然成为“最佳”,每次最佳分配后会留下很小的难以利用的内存块,会产生最多的外部碎片。

4)最坏适应算法(Worst Fit):
算法思想: 为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
算法实现: 空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。在这里插入图片描述
优点: 这样使分给作业后剩下的空闲分区比较大,足以装入其他作业。
缺点: 由于最大的空闲分区总是因首先分配而被划分,当有大作业到来时,其存储空间的申请会得不到满足。

④几种算法总结:

科学家Knuth和Shore对FF、BF、WF三种方法对内存空间的利用情况做了模拟实验。

结果: 一般情况而言:FF优于BF优于WF,宁外在算法实现过程中,BF和WF要对可用块进行排序或者遍历查找,而FF和NF,只需要简单的查找。这告诉我们:设计操作系统时候,不仅仅考虑内存利用率,算法的开销也是一个重要因素。
在这里插入图片描述

(4)分区的回收:

这个王道书上都没有提到,摘录自天勤,很简单,大概看下就行:
在这里插入图片描述

(5)分区的动态管理:

在这里插入图片描述

①紧凑技术:

碎片: 是指内存中无法被利用的存储空间。在分区存储管理方式下,系统运行一段时间后,内存中的碎片会占据相当多的数量空间。

紧凑: 将存储器中所有已分配分区移动到主存的一段,使本来分散的多个小空闲区连成一个大的空闲区。这种通过移动把多个分散的小分区拼接成一个大分区的方法叫做:紧凑

紧凑时机:
1)某个分区回收时立即进行紧凑,这样在主存中总是只有一个连续的空闲区。但是拼接很费时,紧凑频率会使得系统开销加大。
2)找到不到足够大的空闲分区且总容量可以满足作业要求时进行紧凑,这种方式的频率比上一种方案低得多,但空闲分区的管理稍微复杂一些。

②动态重定位分区分配技术:

动态重定位分区分配算法与动态分区分配算法基本相同。差别在于:这种算法增加了紧凑功能,通常是找不到足够大的空间分区满足作业要求,而系统中空闲分区容量总和大于作业要求进行紧凑。

(6)动态分区分配总结:

优点:
1)实现了多道程序共用主存(共用是指多进程同时存在于主存中的不同位置);【对比固定分区分配】
2)管理方案相对简单、不需要更多开销;
3)实现存储保护的手段比较简单
缺点:
1)主存利用不够充分,存在外部碎片
2)无法实现多进程共享存储器信息(共享是指多进程都使用同一个主存段);
3)无法实现主存的扩充,进程地址空间受实际存储空间的限制;【相比虚拟内存】

四、连续分配方式总结

在这里插入图片描述

相关文章
|
4天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
30 12
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
65 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
53 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
3月前
|
关系型数据库 MySQL
MySQl优化:使用 jemalloc 分配内存
MySQl优化:使用 jemalloc 分配内存
|
3月前
|
缓存 Java 编译器
Go 中的内存布局和分配原理
Go 中的内存布局和分配原理
|
4月前
|
存储 缓存 算法
(五)JVM成神路之对象内存布局、分配过程、从生至死历程、强弱软虚引用全面剖析
在上篇文章中曾详细谈到了JVM的内存区域,其中也曾提及了:Java程序运行过程中,绝大部分创建的对象都会被分配在堆空间内。而本篇文章则会站在对象实例的角度,阐述一个Java对象从生到死的历程、Java对象在内存中的布局以及对象引用类型。
127 8
|
4月前
|
NoSQL Redis C++
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
|
4月前
|
Java 运维
开发与运维内存问题之在堆内存中新创建的对象通常首先分配如何解决
开发与运维内存问题之在堆内存中新创建的对象通常首先分配如何解决
24 1
|
3月前
|
存储 NoSQL Java
Tair的发展问题之Tair对于不同存储介质(如内存和磁盘)的线程分配是如何处理的
Tair的发展问题之Tair对于不同存储介质(如内存和磁盘)的线程分配是如何处理的
|
4月前
|
存储 安全 Java
Java面试题:Java内存管理、多线程与并发框架:一道综合性面试题的深度解析,描述Java内存模型,并解释如何在应用中优化内存使用,阐述Java多线程的创建和管理方式,并讨论线程安全问题
Java面试题:Java内存管理、多线程与并发框架:一道综合性面试题的深度解析,描述Java内存模型,并解释如何在应用中优化内存使用,阐述Java多线程的创建和管理方式,并讨论线程安全问题
45 0