iOS 内存管理(一)
iOS 内存管理(一)
一个APP使用了多少内存。这个内存是一个非常模糊的概念,因为内存一般是由几种不同部分组成的。这里我们就首先了解一下,内存一般指的是什么,都有哪些类型。
内存类型
虚拟内存和物理内存
一般系统APP的内存,都包含这样两个部分,一个是虚拟内存,一个物理内存。
物理内存
设备内部的物理内存条上的内存空间。iPhone 7和8之前的手机只有1/2G 内存,iPhone 8p-iPhone12手机一般是3、4、最近新出的iPhone13 达到6GB的内存。
如果系统要访问地址不在物理内存中时,此时发生缺页中断。需要把对应的虚拟内存映射到物理内存中再使用。
一般一个APP最多物理内存的使用率到总内存的60-70%时,就会发生OOM。
虚拟内存
是一种计算机系统的内存管理技术。虚拟内存为每个进程提供了一个独立的、私有的、连续的逻辑地址空间。
32位设备,这个地址范围是4GB,对于64位设备来说,是16EB。对于现有的苹果手机来说,基本都是64位。
通过虚拟内存,我们可以建立起逻辑地址到物理地址的映射,。
虚拟内存到物理内存的映射
每一个进程的虚拟内存都有一个逻辑地址空间,当访问的page在物理内存中,就直接执行,如果不在物理内存时,就会从虚拟内存中把对应的page调入到物理内存中。如果此时物理内存满了,不能再调入新的page了,那么就把暂时不需要的page调出到物理内存,腾出足够的内存空间,再将要访问的page置换调入到内存中,让程序得以继续执行。如果暂时腾不出需要的空间,就会发生OOM
虚拟内存的这个调入/出和置换的功能,使得物理内存得以扩充。
页式存储
分页存储管理是将一个进程的这个逻辑地址空间,也就是虚拟内存地址空间,分成多个大小相等的片,并且给各页进行编号,如第0页、第1页。。。
页面的大小一般是2的幂。如位移量是几位,页面大小就是2的几次幂
分页的地址结构就是
页号 | 位移量 |
---|
页面大小合理,可以提高内存的使用效率。
相应的,物理内存空间也会分成和页面大小一致的若干个存储块,并且依次编号。为了区别期间,我们这里把虚拟内存空间的存储单元称为页及其页号。物理内存中存储单元称为块和块号。
页表
要实现逻辑地址空间到物理内存的映射,需要配置一张页表,其结构如下:
页号 | 存取状态 | 块号 |
---|
段式存储
页是信息的物理单位
段是信息的逻辑单位
为了满足用户(程序员)在编程和使用上的要求,引入了段式存储。优点:
- 方便对共享和保护特定段的信息
- 可以比较方便的动态增长某个段,比如数据段的增长
- 可以动态链接某个段,比如在程序运行过程中需要调用某个段时,再将该段调入内存进行链接。
每个段都有自己的编号,并且可以起个段名字。因为每个段的长度是由其相应的逻辑信息组的长度决定的,所以每个段的段长是不同的。
段的地址结构,如下
段号 | 段内地址 |
---|
段表
要实现逻辑段到物理内存的映射,需要配置一张段表寄存器和段表,其中,段表寄存器结构如下:
段表起始地址 | 段表长度 |
---|
段表结构如下:
段号 | 存取状态 | 段长 | 段基址 |
---|
分页和分段二者都是基于离散分配的内存分配方式,也就是说直接将内存可以分配到许多不相邻的空间分区中。离散分配的基本单位是页的话,就是分页存储方式;离散分配的单位是段的话,就是分段存储方式。
段页式存储
分段分页各有优缺点,分页可以提高内存利用率,减少内存浪费。而分段可以更好满足我们用户的需要。结合这两者,各取所长,结合而成的一种新的存储管理方式系统,就是段页式存储系统,既可以实现分段共享,便于信息保护,动态链接,也可以减少内存碎片浪费的问题。
原理
先将程序分成多个段,比如数据段、栈段、堆段,每个段都有自己的编号,并且可以起个段名字。每个段都从0开始给地址编号,并且是一段连续的地址空间。段内又分成多个页,每个页面的大小为16KB。因此在段页式存储系统中,地址结构是由段号、段内页号、以及页内地址构成的。如下图:
未命名文件 4.png
未命名文件 5.png
地址变换过程
在段页式系统中,为了便于实现虚拟内存地址到物理内存地址的变换,需要配置:
- 段表寄存器,其中存放的是段表的起始地址和段表长度TL。
- 段表:
- 页表,这个页表记录了逻辑地址空间中的所有的页(0~n),在内存中对应的物理块号。实现了页号到物理块号的地址映射。
进行地址变换的时候,
- 首先利用段号S和这个段表长度进行比较。如果S<TL,表示未越界,那么就用这个段表的起始地址和段号,求出该段所对应的段在段表中的位置,也就是这个段中的页表的起始地址;若越界,则程序中断。
- 然后利用逻辑地址中的段内页号P查找对应页的页表项位置;
- 然后再用该页所在的物理块号b和页内地址来构成物理地址;
具体过程如下图:
未命名文件 8.png
在段页式系统中,为了获得一条指令或者数据,需要三次访问内存。
第一次访问,是访问内存中的段表,从中获取页表的地址;
第二次访问是访问内存中的页表,从中获取该页所在的物理块号,并将该块号和页内地址,一起形成指令或数据的物理地址;
第三次才是真正的访问第二次访问所得的地址,取得指令或者数据。