【操作系统 · 内存】内存管理

一、概述

在单道程序设计系统中,内存划分为两部分:

  • 一部分供操作系统使用(驻留监控系统、内核)
  • 一部分供当前正在执行的程序使用

在多道程序设计系统中,必须细分出 “用户” 部分,以满足多个进程的要求。
细分的任务由操作系统动态完成,称为 内存管理(memory management)

内存管理术语:

术语解释说明
页框内存中固定长度的块
固定长度的数据块,存储在二级存储器中(如磁盘)
数据页可以临时复制到内存的页框中
变长数据块,存储在二级存储器中(如磁盘)
整个段可以临时复制道到内存的一个可用区域中(分段),或可以将一个段分为许多页,然后将每页单独复制到内存中(分段、分页相结合)

二、需求

1. 重定位

在多道程序设计系统中,可用的内存空间通常被多个进程共享,我们并不知道执行期间会有哪些程序驻留在内存中,为了方便程序在内存与磁盘间交换,我们需要把进程 重定位(relocate) 到内存的不同区域。

重定位要求操作系统知道 进程控制信息执行栈 的位置,以及该进程开始执行程序的入口点。操作系统负责管理内存,允许程序通过交换技术在内存中移动;处理器负责程序的内存访问。此外,跳转指令 包含下一步要执行的指令的地址,数据访问指令包含被访问数据的 字节/字的地址。

由 操作系统 / 处理器提供 在 逻辑地址 与 实际物理地址 间互相转换的能力。
进程寻址需求

2. 保护

每个进程都应受到保护,以免被其他进程有意/无意地干扰。因此,其他进程不能未经授权地读/写该进程的内存单元(满足重定向会增大满足保护的难度)

内存保护需由处理器(硬件)而非操作系统(软件)满足,因为操作系统不能预测程序所有可能产生的内存访问。因此,只有在指令访问内存时判断访问是否合法。

3. 共享

保护机制应具有一定灵活性,允许多个进程访问内存的同一部分或同一副本,只要不损害基本保护,我们允许对内存共享区域进行受控访问,支持重定位的机制也同时支持共享。

4. 逻辑组织

计算机系统中的内存总是被组织成线性的地址空间,外部存储器在物理层也按类似方式组成。但实际中,会将程序按照模块组织,这其中会带来很多好处:

  1. 独立地编写、编译模块,运行时解析模块之间的引用
  2. 通过适度的额外开销,为不同模块提供不同保护级别(只读、只写、只执行)
  3. 引入某种机制,使模块可被多个进程共享,共享更加方便。

5. 物理组织

计算机存储器至少组织为两级:内存、外存。

操作系统关注内存、外存间的信息流组织,但让程序员负责组织信息流是不切实际的:

  • 供程序、数据使用的内存可能不足,此时程序员必须采取 覆盖(overlaying) 技术来组织程序、数据,这不利于开发。
  • 在多道程序设计环境中,程序员在编写代码时并不知道可用空间的大小、位置。

所以,这一工作应由操作系统负责,这也是存储管理的本质。

三、分区

分区分为固定分区、动态分区,以及一个特殊的系统:伙伴系统

1. 固定分区

大多数内存管理方案都假定操作系统占据内存中某些固定部分,而内存其余部分则供多个用户进程使用。
分区大小
有两种分区策略:大小相等分区、大小不等分区

大小相等固定分区有两个难点:

  • 程序可能太大不能放入一个分区中,需使用覆盖技术。
  • 内存利用率非常低,存在空间浪费,这种现象称为 内部碎片(internal fragmentation)

使用大小不等固定分区可以缓解问题,但无法完全解决,可以使得内部碎片更小、更少,但仍存在内部碎片。

64MB内存分区示例:
64内存分区示例

放置算法
对于同大小分区策略,进程放置策略非常简单:
每个进程分配到最小分区中,每个不同大小分区各自维护一个调度队列,预先匹配队列。这样的优点:内部浪费的空间(内部碎片)最少。

一种更可取的方法:
为所有进程提供一个队列,以避免分区闲置。将进程装入内存时,选择可容纳的最小可用分区,若都被占据则必须进行交换,可以考虑优先级等其他因素。

固定分区缺点:

  • 分区数量在系统生成阶段已经确定,因此限制了活动(未挂起)进程的数量
  • 由于事先设定了分区大小,小作业不能能有效的利用分区空间,有时非常低效。

固定分区中的内存分配

2. 动态分区

对于动态分区,长度、数量可变,但随着时间的推移,内存中形成了越来越多的碎片,内存率随之下降,这种现象称为 外部碎片(external fragmentation),指在分区外形成了许多碎片。而克服外部碎片的技术称为 压缩(compaction):操作系统不时地移动进程,使得占用空间连续。
动态分区

放置算法
可供考虑的放置算法有三种:

  • 最佳适配(Best-fit):选择与要求大小最接近的块
    特点:性能最差,会浪费最小的存储空间,会形成许多极小的碎片,需要更频繁的压缩
  • 首次适配(First-fit):从头开始扫描内存,选择大小足够的第一个可用块
    特点:简单、速度快,但通常位于存储前端会出现很多小碎片
  • 下次适配(Next-fit):从上次放置位置开始扫描,选择下一个大小足够的可用块
    特点:通常位于存储末尾的最大空闲块分裂为小碎片

置换算法
为避免内存中所有进程都处于阻塞态、压缩后仍无可用空间情况,放置处理器等待解除阻塞的时间浪费,需将阻塞进程换出内存,将新进程或处于就绪-挂起态的进程让出内存空间,因此操作系统需知道应使用替换算法替换哪个进程。

3. 伙伴系统

固定分区限制了活动进程数量;动态分区的维护特别复杂,需引入压缩的额外开销。一种折中的方案是伙伴系统(改进型应用于UNIX内核存储分配):

伙伴系统可用内存块大小为 2k 个字,L ≤ K ≤ U,其中 2L 表示分配的最小块尺寸,2U 表示分配的最大块的尺寸,通常 2U 是供分配的整个内存大小。

最初,整个空间视作 2U 的块。

拆分:若请求的大小s满足 2U-1 <s ≤ 2U ,则分配整个空间。否则,该块分成两个大小相等的伙伴,大小均为 2U-1 。若有 2U-2 <s ≤ 2U-1 ,则给请求分配伙两个伙伴中的任何一个;否则,其中一个伙伴又被分成两半,依此类推,直至产生大于等于s的最小块分配给该请求。

合并:我们为 2i 大小的 “空洞” 维护一个列表,空洞可通过对半分裂从 i+1 列表中移出。当一对伙伴均处于 “未分配” 状态时,我们将它们从合并为 i+1 列表中的一个块。

伙伴系统

伙伴系统的分配情况可以用二叉树表示:

伙伴系统-二叉树表示

4. 重定位

简单重定位加载器:首次加载一个进程时,相对内存访问将被绝对地址代替,绝对地址由进程被加载到的基地址确定。‘

首先创建一个进程映像,它被装入内存中的某个分区。每次换入、换出可能会指定到不同的分区。此外,压缩时,内存中的进程也可能发生移动,所以进程位置不固定。

重要概念:

  • 逻辑地址(logical address):当前数据在内存中的物理分配地址无关的访问地址,执行访问前必须转换为物理地址。
  • 相对地址(relative address):逻辑地址的特例,相对于某些已知点(通常是程序的开始处)的存储单元。
  • 物理地址(physical address):绝对地址,数据在内存中的实际位置。

硬件支持:程序装入内存 / 映像换入 时,必须设置的两个寄存器:

  • 基址寄存器:程序在内存中的起始地址。
  • 界限寄存器:程序在内存中的终止位置。

运算步骤:

  1. 基址寄存器的值加上相对地址,产生一个绝对地址。
  2. 将结果与界限寄存器的值比较,若在界限范围内继续执行,否则向操作系统发出一个中断,操作系统应对错误做出响应。

进程映像根据基址、界限寄存器内容隔离,以免其他进程越权访问。

重定位

四、分页

使用固定分区会产生内部碎片,使用不等分区会产生外部碎片。我们可以将内存划分为大小固定、相等 称为 页框(frame) 的可用块,可以将进程中称为 页(page) 的块分配其中。这样内部碎片数量减少,没有外部碎片。

操作系统需要为 每一个进程 + 空闲页框 维护 页表(page table),其中包含进程每页所对应页框位置,即逻辑地址(页号,偏移量),可以据此产生物理地址(页框号,偏移量)。

进程的空闲帧分配:
进程的空闲帧分配
载入进程D时的数据结构(页表):
页表-数据结构
为了使方案更加简单,规定页、页框大小必须为2的幂,以便容易表示其相对地址,使用硬件转换也相对简单。相对地址可由 页号 + 偏移量 表示。

步骤:
考虑一个 n + m 位的地址,最左边 n 位是页号,最右边 m 位是偏移量:

  1. 提取页号
  2. 以此页号为索引,查找相应页框号 k
  3. 页框的起始物理地址 k × 2m ,被访问字节的物理地址 = 起始物理地址 + 偏移量。

五、分段

细分用户程序的另一种可选方案是分段,采用分段技术,可以把程序和与其相关的数据划分到几个 段(segment) 中。尽管段有最大长度限制,但并不要求所有段长相等。

同样,操作系统需要为 每一个进程 + 空闲页框 维护 段表(segment table),其中包含进程每页所对应段框位置,即逻辑地址(段号,偏移量),可以据此产生物理地址(段框号,偏移量)。

逻辑地址:
逻辑地址
逻辑地址 → 物理地址:
逻辑地址 → 物理地址

步骤:
考虑一个 n + m 位的地址,最左边 n 位是段号,最右边 m 位是偏移量:

  1. 提取段号
  2. 以此段号为索引,查找该段的起始物理地址
  3. 使用偏移量与段长进行比较,若 偏移量>段长,则地址失效
  4. 物理地址 = 该段起始物理地址 + 偏移量

小结:

内存管理把内存视为资源,它可以分配、共享给多个活动进程。

  • 分页:划分为相对较小、大小固定的页
  • 分段:划分为大小不同的段
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java操作系统内存管理是指在Java程序运行过程中,如何对系统内存进行合理分配和管理,以提高程序的性能和稳定性。 首先,Java使用垃圾回收机制来管理内存。当Java程序创建对象时,会在堆内存中分配内存空间。当对象不再被引用时,垃圾回收机制会自动回收这些内存空间,减少内存泄漏的发生。 其次,Java通过虚拟机(JVM)来管理内存。JVM会将内存分为不同的区域,包括堆内存、栈内存和方法区等。堆内存用于存放对象实例,栈内存用于存放局部变量和方法调用栈,方法区用于存放类信息和静态变量。通过合理设置JVM参数,可以调整内存区域的大小,以满足程序的需要。 此外,Java还提供了一些内存管理的工具和技术。例如,通过使用弱引用、软引用和虚引用,可以更灵活地管理对象的生命周期。通过使用内存映像文件,可以在程序运行过程中保存对象状态,以便在下次启动时加载。通过使用内存监控工具,可以实时跟踪和分析程序的内存使用情况,以及识别内存泄漏问题。 在编写Java程序时,我们也可以采取一些内存管理的最佳实践。例如,及时释放不再使用的对象引用,尽量避免创建过多的临时对象,避免过度使用静态变量和全局变量等。这些实践可以有效地减少内存的占用,提高程序的执行效率。 总之,Java操作系统内存管理是通过垃圾回收机制、虚拟机和一些工具技术来有效地分配和管理内存,以提高程序的性能和稳定性。编写Java程序时,我们也可以采取一些内存管理的最佳实践,以优化内存使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值