iOS 虚拟内存

虚拟内存简介

什么是VM Regions呢?要知道这个首先要了解什么是虚拟内存。当我们向系统申请内存时,系统并不会给你返回物理内存的地址,而是给你一个虚拟内存地址。每个进程都拥有相同大小的虚拟地址空间,对于32位的进程,可以拥有4GB的虚拟内存,64位进程则更多,可达18EB。只有我们开始使用申请到的虚拟内存时,系统才会将虚拟地址映射到物理地址上,从而让程序使用真实的物理内存。

内存分页

系统会对虚拟内存和物理内存进行分页,虚拟内存到物理内存的映射都是以页为最小粒度的。在OSX和早期的iOS系统中,物理和虚拟内存都按照4KB的大小进行分页。iOS近期的系统中,基于A7和A8处理器的系统,物理内存按照4KB分页,虚拟内存按照16KB分页。基于A9处理器的系统,物理和虚拟内存都是以16KB进行分页。系统将内存页分为三种状态。

活跃内存页(active pages)- 这种内存页已经被映射到物理内存中,而且近期被访问过,处于活跃状态。
非活跃内存页(inactive pages)- 这种内存页已经被映射到物理内存中,但是近期没有被访问过。
可用的内存页(free pages)- 没有关联到虚拟内存页的物理内存页集合。
当可用的内存页降低到一定的阀值时,系统就会采取低内存应对措施,在OSX中,系统会将非活跃内存页交换到硬盘上,而在iOS中,则会触发Memory Warning,如果你的App没有处理低内存警告并且还在后台占用太多内存,则有可能被杀掉。

VM Region

为了更好的管理内存页,系统将一组连续的内存页关联到一个VMObject上,VMObject主要包含下面的属性。
Resident pages - 已经被映射到物理内存的虚拟内存页列表
Size - 所有内存页所占区域的大小
Pager - 用来处理内存页在硬盘和物理内存中交换问题
Attributes - 这块内存区域的属性,比如读写的权限控制
Shadow - 用作(copy-on-write)写时拷贝的优化
Copy - 用作(copy-on-write)写时拷贝的优化
我们在Instruments的Anonymous VM里看到的每条记录都是一个VMObject或者也可以称之为VM Region

堆(heap)和 VM Region

Instruments中有一个VM Track模版,可以帮助我们清楚的了解heap和VM Region的关系
运行Profile,选择Allocation模版,进入后再添加VM Track模版
s1
下面是我自己写的一个测试用的工程,选择Regions Map选项可以看到内存分配的详细信息

s2
我们在VM Track中可以看到,一个VM Region有4种size。
Dirty Size
Swapped Size
Resident Size
Virtual Size
Virtual Size顾名思义,就是虚拟内存大小,将一个VM Region的结束地址减去起始地址就是这个值。Resident Size指的是实际使用物理内存的大小。Swapped Size则是交换到硬盘上的大小,仅OSX可用。Dirty Size根据官方的解释我的理解是如果一个内存页想要被复用,必须将内容写到硬盘上的话,这个内存页就是Dirty的。下面是官方对Dirty Size的解释。secondary storage可以理解为硬盘。
The amount of memory currently being used that must be written to secondary storage before being reused.
所以一般来说app运行过程中在堆上动态分配的内存页都是Dirty的,加载动态库或者文件内存映射产生的内存页则是非Dirty的

点击Address Range列的箭头可以看到更详细的内容,这里进入的是SMALL类型的
s3
总的来说,堆区会被划分成很多不同的VM Region,不同类型的内存分配根据需求进入不同的VM Region。除了MALLOC_LARGE和MALLOC_SMALL外,还有MALLOC_TINY, MALLOC metadata等等

VM Tracker列属性解析

s4
% of Res, 当前Type的VM Regions总Resident Size占比
Type,VM Regions的Type,All和Dirty算是统计性质的Type,TEXT表示代码段的内存映射,DATA表示数据段的内存映射。MALLOC_TINY,MALLOC_LARGE,CG Image等Type可以从VM Region的Extend Info中读取出来
# Regs,当前Type的VM Region总数
Path,VM Region是从哪个文件映射过来,因为有些类似于__DATA和mapped file的内存块是从文件直接映射过来的。
Resident Size,使用的物理内存量。
Dirty Size,使用中的物理内存块如果不交换到硬盘保存状态就不能复用,那么就是Dirty的内存块,比如你主动malloc出来的内存块,如果不保留其中的状态就把它给别人用,那你肯定就无法恢复这个内存块的信息,所以它是Dirty的。如果是一个映射到内存的文件,就算使用它的内存块,还是可以重新从磁盘载入文件到内存的,所以是非Dirty的,比如最上面图中的mapped file那一行,你可以看到Dirty Size是0。
Swapped Size, 在OSX中,不活跃的内存页可以被交换到硬盘,这是被交换的大小。在iOS中,只有非Dirty的内存页可以被交换,或者说是被卸载。
Virtual Size,VM Regions所占虚拟内存的大小
Res. %,Resident Size在Virtual Size中的占比

参考:

https://www.jianshu.com/p/553f34b03624
https://www.jianshu.com/p/f82e2b378455
https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html