「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。
编写OS X和iOS高性能代码的一个重要方面就是高效的内存管理。 今天,我们就来聊聊iOS是如何管理内存的。
内存管理
说到内存管理,iOS的开发小伙伴可能会最先联想到苹果中 MRC (手动引用技术)或者 ARC (自动引用技术),在 ARC 以前,开发者们都是通过 MRC 手动调用管理内存的方法来管理内存,苹果也意识到这种方式会浪费开发者太多的时间在内存管理上,所以开发了 ARC 技术,ARC 是通过编译器来自动调用管理内存的方法。但是在 ARC 中,并不是万能的,有些时候也需要使用自己去管理内存,并且同样也需要注意循环引用。
想要理解内存管理,可不仅仅要从引用计数算法,和循环引用着手,同时还要了解内存管理的前世今生。
前世今生
最初的时候,程序是直接操作物理内存,在慢慢的演进中,出现了多程序后就出现了一些问题。什么问题呢?由于程序直接访问和修改物理内存,所以也可以修改其他程序所使用的物理内存,这样在程序运行时就无法保证其数据的安全性。
为了解决多程序多任务同时运行的问题,所以在程序访问物理内存之间增加了一层中间层,这个中间层通过映射,把管理的地址映射到物理内存上。这个中间层也就是我们现在所说的虚拟内存。
虚拟内存 (VM for Virtual Memory)
有了虚拟内存,可以大大节省物理内存的使用,并且摆脱了物理RAM的限制。
虚拟内存会为每个进程创建一个逻辑地址空间(虚拟内存空间),并将这些地址空间分割成了大小一致的块,这样的块称之为页(Page)。进程和它的内存管理单元(MMU)维护了一个页表Page Table),这个页表将程序的逻辑内存地址空间和实际的物理内存地址空间做了映射。当程序代码访问内存地址(逻辑地址)时,内存管理单元会负责将逻辑地址转换为实际的物理地址。这种转换是自动进行的并对正在运行的app是透明的。
Page
分页(Page)就是把地址空间切分成固定大小的单元,这样我们就不用去考虑堆和栈会具体申请多少空间,而只要考虑需要多少页就可以了。这对于操作系统管理来说也会简单很多,只需要维护一份页表(Page Table)来记录虚拟页(Virtual Page)和物理页(Physical Page)的关系即可。
但是,虚拟页和物理页的个数是不一样的。比如,在 64 位操作系统中使用的是 48 位寻址空间,之所以使用 48 位寻址空间,是因为推出 64 位系统时硬件还不能支持 64 位寻址空间,所以就一直延续下来了。虚拟页大小是 16K,那么虚拟页最多能有 2^48 / 2^14 = 16M 个,物理内存为 16G 对应物理页个数是 2^64 / 2^14 = 524k 个。
维护虚拟页和物理页关系的页表会随着进程增多而变得越来越大,当页表大于寄存器大小时,就无法放到寄存器中,只能放到内存中。当要通过虚拟地址获取物理地址的时候,就要对页表进行访问翻译,而在内存中进行访问翻译的速度会比 CPU 的寄存器慢很多。
MMU 中有一个 页表缓冲(TLB - Translation-Lookaside Buffer),可以作为缓存加速访问。所以,在访问页表前,会首先检查 TLB 有没有缓存的虚拟地址对应的物理地址:
- 如果有的话,就直接返回,而不用访问页表了;
- 如果没有的话,就需要继续访问页表。
如果每次都要访问整个列表去查找物理地址,难免还是会影响效率,所以引入了多级页表技术。这样也就能根据一定的算法灵活分配多级页表,保证一级页表最小的内存占用。其中,一级页表对应多个二级页表,再由二级页表对应虚拟页。
这样内存中只需要保存一级页表就可以,不仅减少了内存占用,而且还提高了访问效率。根据多级页表分配页表层级算法,空间占用多时,页表级别增多,访问页表层级次数也会增多,所以多级页表机制属于典型的支持时间换空间的灵活方案。
Page In/Out
由于虚拟内存的空间远远大于物理内存,在任意一个时间点,虚拟内存中的一个页并不一定总是在物理内存中,而是可能被暂时存到了磁盘上,这样物理内存便可以暂时释放这部分空间,供优先级更高的任务使用,因此磁盘可以作为备份存储以扩展物理内存(OS 中有,iOS 没有)。另一种可能是加载一个比较大的文件/动态库,每次使用我们可能只需要加载其中的一部分,那么就可以使用 mmap 映射这个文件到虚拟内存空间,这样当我们访问其中一部分时,系统会自动把这一部分从磁盘加载到内存,而不加载其余部分。
这样把磁盘中的数据写到内存/从内存中写回磁盘成为 page in/out。
为了减少磁盘空间占用,iOS 采用了 Jetsam 机制来控制内存的使用。(iOS上不支持内存交换技术,OX是支持的)
最后
在iOS中占用内存过多的进程会被强杀,这也就对 App 占用的内存提出了更高的要求。同时,Jetsam 机制也可以避免磁盘和内存交换带来的效率问题。