前言
我们在查看 iOS 应用内存时, 最常见的手法就是查看左边的 Debug Navigator 不知你是否也曾困惑于这个内存究竟包括哪些部分, 或者使用 Allocations 模版观察内存时发现无法和 Debug Navigator 显示的内存匹配上, 这篇文章将带你解答这些疑惑
Debug Navigator VS Allocations
我们运行一个很简单的 iOS App, 我只在 ViewController 中放置了一个 View, 然后对比下 Debug Navigator 和 Allocations 给出的内存用量
可以发现, Debug Navigator 给出的是 79.3M, 而 Allocations 统计的所有堆和相关 VM 加起来才 38.72M, 相差的还是很多的在之前的文章中我有介绍关于 Allocations 和 VM Tracker 的深入理解, 其实 Allocations 中主要包含的是所有 MALLOC_XXX VM Region 和部分 App 进程创建的 VM Region 非动态的内存, 以及部分其他动态库创建的 VM Region 并不在 Allocations 的统计范围内比如主程序或者动态库的_DATA 数据段, 这些数据内存区域并非通过 malloc 分配, 也就没有统计在 All Heap Allocations 中, 所以你会发现 All Heap Allocations 往往会比较小除非你自行使用 malloc 系列方法创建大内存块, 否则很难看到 All Heap Allocations 有一个大的数值我们在实际的 App 中, 大的内存占用一般都是类似于 webKit,ImageIO,CoreAnimation 等虚拟内存区域 (VM Region), 这些 VM Region 一般由系统代码生成和管理, 我们编写的代码如果间接引用了这些内存而没有释放, 也就会造成大面积的内存泄漏
Debug Navigator VS VM Tracker
接下来我们来看看 VM Tracker 统计的内存如何, 下面是截图
在看 VM Tracker 时, 我们主要看 Dirty Size 和 Swapped Size, 由于我是在模拟器上调试的, 所以才需要关注 Swapped Size, 在手机上, 主进程的内存应该是不会交换到硬盘上的, 内存不足时, 会触发内存警告 Dirty Size 主要指的是不可被重新载入的内存区域大小, 比如函数栈, 如果你把函数栈的数据给抹掉了, 也就无法恢复之前的函数调用栈数据了, 这种可以称为 Dirty 内存区域, 但如果是通过文件内存映射载入到内存区域的, 可以先清除掉这部分内存里的数据暂时把这部分内存给别人用, 需要时再从文件载入到内存, 这种内存区域可以认为是非 Dirty 的 Dirty Size 可以代表一个进程需求的最少内存量, 当然在模拟器上, 还要加上被交换出去的数据大小, 即 Swapped Size
我们回到上图, VM Tracker 给出的 Dirty Size 总量时 69.79M, 还是和 79.3M 有些差距不过我们可在在图中看到_DATA 数据段, Stack(函数栈) 等等 Allocations 没有统计的内存区域
Debug Navigator VS vmmap command line
苹果除了 Instrument 的 VM Tracker 可以查看虚拟内存之外, 还有一个 vmmap 命令行可以查看进程的虚拟内存分配使用模拟器启动 App, 通过 Activity Monitor 找到 App 的进程 ID, 比如 1364, 使用 vmmap 查看它的虚拟内存分配
vmmap 1364
结果如下
- ...
- VIRTUAL RESIDENT DIRTY SWAPPED VOLATILE NONVOL EMPTY REGION
- REGION TYPE SIZE SIZE SIZE SIZE SIZE SIZE SIZE COUNT (non-coalesced)
- =========== ======= ======== ===== ======= ======== ====== ===== =======
- Activity Tracing 256K 36K 36K 12K 0K 36K 0K 2
- CoreAnimation 36.1M 33.3M 33.3M 2848K 0K 33.3M 0K 2
- Kernel Alloc Once 8K 8K 8K 0K 0K 0K 0K 2
- MALLOC guard page 48K 0K 0K 0K 0K 0K 0K 13
- MALLOC metadata 260K 92K 92K 32K 0K 0K 0K 16
- MALLOC_LARGE 4360K 4256K 4256K 104K 0K 0K 0K 3 see MALLOC ZONE table below
- MALLOC_LARGE (empty) 3988K 2080K 2080K 1908K 0K 0K 0K 2 see MALLOC ZONE table below
- MALLOC_LARGE metadata 4K 4K 4K 0K 0K 0K 0K 2 see MALLOC ZONE table below
- MALLOC_NANO 16.0M 2160K 2160K 0K 0K 0K 0K 3 see MALLOC ZONE table below
- MALLOC_SMALL 40.0M 840K 840K 356K 0K 0K 0K 3 see MALLOC ZONE table below
- MALLOC_TINY 8192K 320K 320K 36K 0K 0K 0K 3 see MALLOC ZONE table below
- Performance tool data 316K 264K 264K 52K 0K 0K 0K 3 not counted in TOTAL below
- STACK GUARD 56.0M 0K 0K 0K 0K 0K 0K 4
- Stack 9232K 76K 76K 20K 0K 0K 0K 7
- __DATA 35.4M 16.6M 16.4M 12.3M 0K 0K 0K 282
- __FONT_DATA 4K 0K 0K 0K 0K 0K 0K 2
- __LINKEDIT 96.0M 69.5M 0K 0K 0K 0K 0K 225
- __TEXT 228.3M 54.4M 4K 4K 0K 0K 0K 225
- __UNICODE 560K 320K 0K 0K 0K 0K 0K 2
- mapped file 28.7M 2116K 0K 0K 0K 0K 0K 3
- shared memory 44K 24K 24K 8K 0K 0K 0K 5
- =========== ======= ======== ===== ======= ======== ====== ===== =======
- TOTAL 562.9M 185.8M 59.3M 17.5M 0K 33.4M 0K 786
我们将 Dirty Size 和 Swapped Size 总量相加 59.3M + 17.5M = 76.8M, 和 Debug Navigator 给的值已经很相近了, 我们再看上面的表格, 发现有一行是这么写的 Performance tool data ... not counted in TOTAL below , Performance tool data 并没有统计在最下面的 TOTAL 中, 因为这些数据是 Debug 时提供调用回溯数据用的, 所以 vmmap 默认认为没有价值, 没有统计但是 Debug Navigator 不这么认为, 我们加上 Performance tool data 的内存用量, 264K + 52K = 316K = 0.308M, 加上之前的 76.8M 就是 77.108M, 由于本次并没有使用 Instruments 进行 profile, 所以占用的内存会少一些, Debug Navigator 显示的刚好是 77.1M 至于为什么 vmmap 显示的数据要比 Instruments VM Tracker 的要完整, 目前我还没有明确的答案
shared memory
最后我要提到的时共享内存, 共享内存可以提供跨进程访问的能力, 不过如果你的 App 使用了别的进程创建的共享内存, 那么 Debug Navigator 是不会将它计入你自己的内存总量的, 不过 vmmap 会将它加入 TOTAL 中, 所以可能会导致 vmmap 计算的内存量会大于 Debug Navigator 统计内存量由于目前 iOS 对于 shared memory 的一些 API 并不支持, 我也没有深入研究, 只是在 OSX 中验证了这一点
总结
最后来总结一下, Debug Navigator 其实就是统计了当前进程的所有虚拟内存的 Dirty Size + Swapped Size, 当然还要剔除掉对第三方共享内存的使用量, 当我们发现 Debug Navigator 的内存量飙高时, 不仅仅要去关注 Heap 上的内存用量, 更要关注 VM Tracker 中那些大 Dirty Size 的 VM Region, 这样才能更透彻的了解你的 App 究竟是怎样使用内存的
来源: https://www.thinksaas.cn/group/topic/839013/