Skip to content
iOS内存管理机制
iOS内存管理6/21/2023

前言

前段时间面试的时候面试官提到了这个问题,完全没了解过的我以“不是很清楚”收场,于是赶紧补了下相关的知识,这才发现以前学习的都太浅了,真正有价值的一直没有了解到,还有很多很多知识要补充。这篇也作为写技术文章的开篇吧。

关于内存管理有两个角度,一个角度是系统级的,有一些更为底层的机制。另一个角度是语言级别的,下面也会学习下Swift的内存管理机制。

为什么要研究内存管理

一个app和其他app的共存情况分为以下几种:

  • app 内存消耗较低,同时其他 app 也非常“自律”,不会大手大脚地消耗内存,那么即使切换到其他应用,我们自己的 app 依然是“活着”的,保留了用户的使用状态,体验较好
  • app 内存消耗较低,但是其他 app 非常消耗内存(可能是使用不当,也可能是本身就非常消耗内存,比如大型游戏),那除了当前在前台的进程,其他 app 都会被系统回收,用来给活跃进程提供内存资源。这种情况我们无法控制
  • app 内存消耗比较大,那切换到其他 app 以后,即使其他 app 向系统申请不是特别大的内存,系统也会因为资源紧张,优先把消耗内存较多的 app 回收掉。用户会发现只要 app 一旦退到后台,过会再打开时就会重新加载
  • app 内存消耗特别大,在前台运行时就有可能被系统 kill 掉,引起闪退

所以总的来说,是为了使用户获得更好的体验,不仅你的 App 会启动得更快,系统会表现得更好,你的 App 和别的App也会在内存中保留更长的时间。

iOS内存管理

首先,每个进程都有一个自己私有的虚拟内存空间,所有进程都只能使用各自的区域,再由MMU把这些虚拟内存映射到物理内存。而虚拟内存是远大于物理内存的,于是在被使用前会先被暂时存在磁盘上。内存管理的基本单位是页(Page),现在为16k,当进程需要被调度时,会把这部分从磁盘加载到内存中,而如果是一个比较大的文件,只需加载器中需要的部分即可,而不加载其他部分。这样把磁盘中的数据写到内存/从内存中写回磁盘成为 page in/out。

系统在分配内存时是给了一个内存页,以堆的形式,可以存储多个对象,也可以跨页存储。一般内存的使用是通过page数量 * page大小来计算。而page分为净页和脏页。

假设我分配了一个含有 20000 个整数的数组,系统可能会分配给我 6 个内存页面,当在开始和末尾分别存入数据后,第一和六页就变成了脏页,而中间的依然为净页,因为app还没有在其中写入数据。

![image-20230624104827339](/Users/jiaolong/Library/Application Support/typora-user-images/image-20230624104827339.png)而如果是只读文件,在写入内存时会一直都是净页,比如jpeg,当他被映射到四个page中的时候,第四个因为没有用完,所以依然可以做其他事,而前三个page会作为净页,可以被系统释放掉。

一个App内存的占用由脏内存段、压缩内存段和净内存段组成。

  • 脏内存则是任何通过app写入的数据
  • iOS 没有传统的磁盘交换系统 取而代之 它使用内存压缩器 它是在 iOS 7 中被引入的内存压缩器 接收未访问的内存页 并压缩它们 这实际上可以创建更多的空间 但在访问时 压缩器会对它们进行解压 以便读取内存
  • 净内存是可以被分页的数据,图片、文件或是框架。

每个 App 都有一个内存占用限制,每个设备的限制也可能不同,而如果超过这个限制就会导致EXC_RESOURCE_EXCEPTION。

Swift内存管理机制

作为一种现代化高级编程语言,Swift为您的应用程序中的分配、释放等内存管理需求提供强有力的支持。它使用的是一种称为自动化引用计数(ARC)的技术。

相关链接:

  1. iOS 内存管理研究
  2. WWDC18 Session 416: iOS Memory Deep Dive