美文网首页杂乱
iOS 操作系统的内存管理

iOS 操作系统的内存管理

作者: _涼城 | 来源:发表于2022-03-30 17:39 被阅读0次

    前言

        内存是所有程序都使用的重要系统资源。程序必须先加载到内存中才能运行,并且在运行时,它们会分配额外的内存(显式和隐式)来存储和操作程序级数据。在内存中为程序的代码和数据腾出空间需要时间和资源,因此会影响系统的整体性能。
        在计算机系统中,内存通常都有操作系统来管理。内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。
        高效的内存管理是在 OS X 和 iOS 中编写高性能代码的一个重要方面。最小化内存使用不仅可以减少应用程序的内存占用,还可以减少它消耗的 CPU 时间。但是,为了正确编写代码,我们就不仅仅要关注用户态接口层面,比如引用计数算法和循环引用监控技巧,还需要了解底层系统如何管理内存,从而熟悉底层的内存管理机制
        苹果在Memory Usage Performance Guidelines - About the Virtual Memory System 提到 OS X 和 iOS 与大多数操作系统一致使用的虚拟内存系统,那么虚拟内存是什么呢?就需要从现代操作系统的内存管理演进说起

    独占式内存管理

        <<现代操作管理系统>>这本书描述了内存管理的演进过程,在20世纪早期的单任务系统中,程序都是直接访问物理内存的,并没有虚拟内存这个抽象概念,同一时间只能有一个应用程序独享所有的内存,也称为独占式内存管理。但后来有了多程序多任务同时运行,就出现了很多问题。
    比如:

    • 同时运行的程序占用的总内存必须要小于实际物理内存大小。
    • 再比如,程序能够直接访问和修改物理内存,也就能够直接访问和修改其他程序所使用的物理内存,程序运行时的安全就无法保障。

    虚拟地址空间

        要保证多个程序同时运行在内存中不受影响,就需要解决两个问题:保护和重定位。为了解决这个问题,虚拟地址空间(virtual address space)诞生了。虚拟地址空间是进程可用于寻址的一套地址集合.每个进程只能看到自己的虚拟地址空间。为了提供每个进程的虚拟地址空间,

    交换技术

        由于内存是有限的,无法保存所有内存,为了处理内存超载的问题,最简单的办法就是使用交换技术即一个进程运行时完整调入内存,空闲时存储在磁盘

    虚拟内存(Virtual Memory)

        尽管虚拟地址空间可以解决直接访问物理内存带来的问题,但是内存大小赶不上软件增长的速度,结果就是需要运行的程序往往大到内存无法容纳。为了解决这个问题,所采取的解决办法称为虚拟内存
        虚拟内存是计算机系统内存管理的一种技术。它使得每个应用程序拥有连续可用的内存(一个连续完整的地址空间),将这个空间分割为许多块,每一块都有连续的地址范围,并将所需的部分装载映射到物理内存中。这样每个程序就只能访问自己的地址空间(Address Space),程序与程序间也就能被安全地隔离开了。有了虚拟内存这样一个中间层,极大地节省了物理内存。

    内存管理单元(MMU)

        对于程序自身而言,它能从虚拟地址空间到物理地址空间的映射,需要一个转换的过程,完成这个转换运算的部件就是 MMU(memory management unit),即内存管理单元

    分段(Segment)

        每个程序都有自己的进程,如果每个进程的内存布局都是连在一起的话,每个进程分配的空间就没法灵活变更,栈和堆没用满时就会有很多没用的空间,造成很大的内存空间浪费。
        段式内存管理解决了程序以单位做内存管理导致的内存利用率不足的问题。段是逻辑上的概念,分段就是将进程里连在一起的代码段、数据段、栈、堆分开成独立的段,每个段内空间是连续的,段之间不连续。由于段不是连续的,MMU 需要为每个段提供一对 base-bound 寄存器。这样,内存的空间管理 MMU 就可以更加灵活地进行内存管理。

    那么,段和进程关系是怎么表示的呢?

        进程中内存地址会用前两个字节表示对应的段。比如 00 表示代码段,01 标识堆。

    段里的进程又是如何管理内存的呢?

    每个段大小增长的方向 Grows Positive 也需要记录,是否可读写也要记录,为的是能够更有效地管理段增长。

    内存碎片

        每个段的大小不一样,在申请的内存被释放后,容易产生碎片,这样在申请新内存时,很可能就会出现所剩内存空间够用,但是却不连续,于是造成无法申请的情况。这时,就需要暂停运行进程,对段进行修改,然后再将内存拷贝到连续的地址空间中。但是,连续拷贝会耗费较多时间。解决内存碎片的方法之一的是定时进行碎片整理,另一个解决方法就是页式内存管理

    分页(Page)

        页式内存管理的思路,是将虚拟内存和物理内存都划分为多个固定大小的区域。这些区域我们称之为 页(Page)。分页就是把地址空间切分成固定大小的单元,这样我们就不用去考虑堆和栈会具体申请多少空间,而只要考虑需要多少页就可以了。这对于操作系统管理来说也会简单很多,只需要维护一份页表(Page Table)来记录虚拟页(Virtual Page)和物理页(Physical Page)的关系即可

        虚拟页的前两位是 VPN(Virtual Page Number),根据页表,翻译为物理地址 PFN(Physical Frame Number)。虚拟页与物理页之间的映射关系,就是虚拟内存和物理内存的关系,如下图所示:

    虚拟页与物理页之间的映射关系

    页表缓冲区 (TLB)

        维护虚拟页和物理页关系的页表会随着进程增多而变得越来越大,当页表大于寄存器大小时,就无法放到寄存器中,只能放到内存中。当要通过虚拟地址获取物理地址的时候,就要对页表进行访问翻译,而在内存中进行访问翻译的速度会比 CPU 的寄存器慢很多。
        那么,怎么加速页表翻译速度呢?我们知道缓存可以加速访问。MMU 中有一个 TLB(Translation-Lookaside Buffer),可以作为缓存加速访问。所以,在访问页表前,首先检查 TLB 有没有缓存的虚拟地址对应的物理地址:

    • 如果有的话,就可以直接返回,而不用再去访问页表了;
    • 如果没有的话,就需要继续访问页表。

    多级页表(Multi-level Page Table)

        每次都要访问整个列表去查找我们需要的物理地址,终归还是会影响效率,为了降低页表大小又引入了多级页表技术。多级页表可以看成是将页表分页,也就是,根据一定的算法灵活分配多级页表,保证一级页表最小的内存占用。其中,一级页表对应多个二级页表,再由二级页表对应虚拟页。这样内存中只需要保存一级页表就可以。

    根据多级页表分配页表层级算法,空间占用多时,页表级别增多,访问页表层级次数也会增多,所以多级页表机制属于典型的支持时间换空间的灵活方案。

    交换区 Swap space

        如果我们可以把那些暂时用不到的页存在磁盘上,等需要时在加载到内存中,就可以节省很多物理内存。磁盘中用来存放这些页的区域也称为交换区(Swap space)

    共享页

        为了避免同一个页在内存中有两个副本,将只读的程序部分页设计为共享页,供多个进程使用。

    内存映射文件

        通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。

    共享库

        共享库实际上是内存映射文件的一个特例,也称为动态链接库,供多个程序使用,可以使得程序可执行文件变得更小。当一个共享库被装载和使用时,整个库不是一次性存入内存中的,而是根据需要,以页为单位装载的,因此没有调用到的函数是不会被装载到内存中的。

    缺页中断(Page Fault)

        在这种机制下,当进程访问一个虚拟内存 Page,而对应的物理内存不存在时,会触发缺页中断(Page Fault),缺页也会导致内存的访问效率降低,因此阻塞进程。

    分段和分页的结合 -- 段页式内存管理

        目前大部分操作系统使用的都是分段和分页的结合的方式,只是主要显示为分页的形式。页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。如果将这两种存储管理方法结合起来,就形成了段页式存储管理方式。

        段页式管理就是将程序分为多个逻辑段,在每个段里面又进行分页,即将分段和分页组合起来使用。这样做的目的就是想同时获得分段和分页的好处,但又避免了单独分段或单独分页的缺陷。

    iOS 操作系统是怎么管理内存的

        OS X 和 iOS 都包含一个无法关闭的完全集成的虚拟内存系统;它始终处于开启状态。虚拟内存允许操作系统摆脱物理 RAM 的限制。虚拟内存管理器创建一个逻辑地址空间(或“每个进程的虚拟地址空间)并将其划分为大小一致的内存块,称为页。处理器及其内存管理单元 (MMU) 维护一个页表将程序逻辑地址空间中的页面映射到计算机 RAM 中的硬件地址。当程序代码访问内存中的地址时,MMU 使用页表将指定的逻辑地址转换为实际的硬件内存地址。这种转换是自动发生的,并且对正在运行的应用程序是透明的。

        App 在运行时,大多数的时间只会使用很小部分的内存,所以使用比段粒度更小的空间管理技术,也就是分页。iOS 的 XNU Mach 微内核中有很多分页器提供分页操作,比如 Freezer 分页器、VNode 分页器。还有一点需要注意的是,这些分页器不负责调度,调度都是由 Pageout 守护线程执行。

        就程序而言,其逻辑地址空间中的地址始终可用。但是,如果应用程序访问当前不在物理 RAM 中的内存页上的地址,则发生页面错误。当这种情况发生时,虚拟内存系统会调用一个特殊的页面错误处理程序来立即响应该错误。页面错误处理程序停止当前执行的代码,定位物理内存的空闲页面,从磁盘加载包含所需数据的页面,更新页表,然后将控制权返回给程序代码,然后程序代码可以访问内存地址一般。这个过程被称为分页

        如果物理内存中没有可用页面,则处理程序必须首先释放现有页面以为新页面腾出空间。系统发布页面的方式取决于平台。在 OS X 中,虚拟内存系统经常将页面写入后备存储。这后备存储是基于磁盘的存储库,其中包含给定进程使用的内存页面的副本。将数据从物理内存移动到后备存储称为分页(或“换出”);将数据从后备存储移回物理内存称为分页(或“换入”)。在 iOS 中,没有后备存储,因此页面永远不会调出到磁盘,但只读页面仍会根据需要从磁盘调入。

        操作系统永远不会从内存中删除可写数据。相反,如果可用内存量下降到某个阈值以下,系统会要求正在运行的应用程序自愿释放内存以为新数据腾出空间。未能释放足够内存的应用程序将被终止。

    相关文章

      网友评论

        本文标题:iOS 操作系统的内存管理

        本文链接:https://www.haomeiwen.com/subject/pyrwjrtx.html