内存管理:从硬件视角解析碎片问题

从硬件角度看内存管理:

1. 动态分区法

动态分区法在开始时是很好的,但是随着时间的推移会出现很多内存空洞,内存的利用率随之下降,这些内存空洞就是我们常说的内存碎片。为了解决碎片化的问题,操作系统需要动态地移动进程,使得进程占用的空间是连续的,并且所有的空闲空间也是连续的。整个进程的迁移是一个非常耗时的过程。

动态分区法存在如下问题:

  1. 进程地址空间保护问题。所有的进程都可以访问全部的物理内存,所以恶意的程序可以修改其他程序的内存数据,这使得进程一直处于危险的状态下。
  2. 内存使用效率低。如果即将运行的进程所需的内存空间不足,就需要选择一个进程以进行整体换出,这种机制导致大量的数据需要换入换出,效率非常低下。
  3. 程序运行地址重定位问题。进程在每次换入、换出时使用的地址都是不固定的,这给程序的编写带来了一定的麻烦,因为访问数据和指令跳转时的目标地址通常是固定的,这就需要重定位技术。

2. 分段机制

分段机制的基本思想是把程序所需的内存空间的虚拟地址映射到某个物理地址空间中。分段机制可以解决地址空间保护的问题,进程A和进程B会被映射到不用的物理地址空间中,它们在物理地址空间是不会有重叠的。因为进程看到的是虚拟地址空间,不关心它实际映射到了哪个物理地址。如果一个进程访问了没有映射的虚拟地址空间,或者访问了不属于该进程的虚拟地址空间,那么 CPU 会捕捉这个越界访问,并且拒绝该次访问。同时 CPU 会发送一个异常给操作系统,由操作系统处理这些异常,这就是我们常说的缺页异常。另外,对于进程来说,它不再需要关系物理地址的布局,它访问的地址是虚拟地址,只需要按照原来的地址编写程序和访问地址,这样程序就可以无缝地迁移到不同的操作系统中了。

分段机制解决问题的思路可以总结为增加一个虚拟内存,进程运行时看到的地址是虚拟地址,因此需要通过 CPU 提供的地址映射方法,把虚拟地址转换成实际的物理地址。这样多个进程同时运行时,就可以保证每个进程的虚拟地址空间是相互隔离的,操作系统只需要维护虚拟地址到物理地址的映射关系就可以。

分段机制是一个比较明显的改进,但是它的内存使用效率依然比较低。分段机制对虚拟内存到物理内存的映射依然以进程为单位。也就是说,当物理内存不足时,通常做法是把这个进程的所有段都换出到磁盘,因此会导致大量的磁盘访问,从而影响系统性能。站在进程的角度来看,把整个进程进行换出/换入的方法还是很麻烦。进程在运行时,根据局部性原理,只有一部分数据是一直使用的,若把那些不常用的数据交换出磁盘,就可以节省很多系统带宽,而那些常用的数据驻留在物理内存中,因此可以得到比较好的性能。于是,人们在分段机制的实践之后又发明了新的机制------分页机制。

3.分页机制

分段机制的地址映射的粒度太大,以整个进程地址空间为单位的分配方式导致内存利用率不高。分页机制把这个分配机制的单位继续细分成固定大小的页面,进程的虚拟地址空间也按照页面来分割,这样常用的数据和代码就可以以页面的为单位驻留在内存中。而那些不常用的页面可以交换到磁盘中,从而节省物理内存,这比分段机制要高效很多。进程以页面为单位的虚拟内存通过 CPU 的硬件单元映射到物理内存中,物理内存也以页面为单位来管理,这些物理内存成为物理页面或者页帧。进程的虚拟地址空间中的页面称为虚拟页面。操作系统为了管理这些页帧需要按照物理地址给每个页帧编号,这个编号称为页帧号。

分页机制的实现离不开硬件的支持,在 CPU 内部有一个专门的硬件单元来负责这个虚拟页面到物理页面的转换,它就是一个称为 MMU 的硬件单元。ARM 处理器的 MMU 包括 TLB 和页表遍历单元两个部件。


内部碎片外部碎片

它们的核心区别在于:碎片产生在已分配的内存区域内部,还是已分配区域之间未使用的空闲内存中

1. 内部碎片

定义 :发生在已分配的内存块内部 的未被利用的空间。即,操作系统或内存管理器分配给一个进程的内存大于该进程实际请求的内存,这多出来的、在进程内部但无法被利用的部分就是内部碎片。

产生原因

  • 固定大小的分区/内存分配:这是最主要的原因。系统采用固定大小的块(如页框、内存池的固定单元)进行分配。例如,系统最小分配单位是4KB,但一个进程只请求了3KB,那么分配给它整个4KB的块后,其中就有1KB的内部碎片。
  • 数据结构对齐:为了提高访问效率,编译器或运行时环境可能会在数据结构成员之间插入"填充字节",导致结构体实际占用的内存大于其成员理论大小之和,这也是一种内部碎片。
  • 分配器开销:内存分配器自身的管理数据(如头信息)有时会存储在分配块内部,这部分空间对用户程序不可用。

特点

  • 存在于已分配单元内部
  • 即使回收这个内存块,碎片也无法被其他请求直接利用(除非新请求的大小正好能放入这个块,且小于等于原进程实际使用的部分,但这通常不可能由分配器精确控制)。
  • 通常无法通过移动/紧凑内存来消除,因为这是分配策略固有的浪费。

简单比喻:你去快递柜取快递。最小的柜子是"大号柜子",但你只买了一个小手机,却不得不占用整个大柜子。柜子内部剩下的空间就是"内部碎片",浪费了但无法用于存放其他物品。


2. 外部碎片

定义 :发生在已分配的内存块之间 的那些分散的、较小的、无法被有效利用的空闲内存块。即,空闲内存的总量足够满足一个新的内存请求,但没有一个单独的空闲块大到可以满足这个请求,因为这些空闲块被已分配的内存块分隔开了。

产生原因

  • 可变大小的分区/动态分配:进程申请和释放不同大小的内存块,在长时间运行后,内存中会散布着许多已分配块和大小不一的空闲块。
  • 分配与释放的顺序:如果先后分配和释放的块大小不匹配,就会产生很多"内存空洞"。

特点

  • 存在于已分配单元之间(即空闲内存中)。
  • 空闲内存总量足够,但无法分配,导致内存利用率下降,甚至分配失败。
  • 可以通过"内存紧凑"技术来消除,即移动已分配的内存块,将所有空闲内存合并成一个大的连续区域。但这个过程开销很大,通常需要重定位支持。

简单比喻:一个长长的停车场上停了许多车(已分配块),车离开后留下了许多分散的空车位(外部碎片)。现在来了一辆大巴士,需要连续5个空位才能停下。虽然总的空车位远超过5个,但它们都不连续,所以巴士无法停放。这就是外部碎片。


对比表格

特性 内部碎片 外部碎片
位置 已分配的内存块内部 已分配的内存块之间(空闲内存中)
产生时机 分配时即产生 经过多次分配和释放后逐渐产生
根本原因 分配块尺寸大于请求尺寸 空闲内存被分割成不连续的小块
是否可被"紧凑"消除 (碎片在块内,移动整个块也无济于事) (通过移动已分配块,合并空闲块)
常见于何种技术 分页系统、固定大小内存池、固定分区 分段系统 、可变分区动态分配(malloc/free
解决方案举例 减小页/块大小,使用更适配的分配单位 紧凑技术,分段分页结合(如段页式),伙伴系统,Slab分配器
相关推荐
知无不研3 天前
内存碎片与内存优化
开发语言·c++·内存优化·内存碎片·内存操作
xiaolingting10 个月前
Redis 与 Java HashMap 扩容负载因子差异解析
数据库·redis·hashmap·负载因子·内存碎片·内存敏感·渐进式扩容
矩阵科学2 年前
Redis 关于内存碎片的解决方法
redis·内存碎片·active-defrag·内存爆满·redis内存碎片
剑海风云2 年前
通过自定义分配器解决 ZGC中的碎片问题
java·算法·并发·zgc·内存碎片·0字节标头·zgc小页面
陈建1112 年前
Redis核心技术与实战【学习笔记】 - 12.Redis删除数据后,为什么内存占用率还是很高?
redis·内存碎片·redis 内存碎片
不脱发的程序猿2 年前
什么是内存碎片?
嵌入式开发·内存碎片