感谢你阅读本文,如果你喜欢的话,欢迎关注「 iOS 成长指北 」,如果本文对你有所帮助,欢迎点赞、收藏、分享
小小文件,大大内容!
在学习 iOS 内存管理的过程中,除了使用 Instruments 中的 Allocations、Leaks之类的工具,我们还可以使用命令行工具来分析 .memgraph 文件。
今天我们主要介绍以下几种命令行分析工具 vmmap、leaks、heap、malloc_history。
获取 Memory Graph 文件
当我们的应用程序运行一段时间后,我们可以通过以下方式来获取一个具有相当数据的 .memgraph 文件。
首选,在运行 APP 过程中,打开 Memory Graph,选择 View Memory Graph Hierarchy

然后,生成完 Memory Graph 以后,点击 File -> Export Memory graph 导出 一个 .memgraph 文件

一旦导出了 .memgraph 文件。我们可以使用命令行工具(如 vmmap、leaks、heap、malloc history)进一步研究它。
vmmap
在 从 OOM 到 iOS 内存详解 一文中,我们简单介绍了使用 vmmap 命令行工具获取 .memgraph 文件的总结信息。
vmmap -summary a.memgraph
使用 -summary 获取当前 a.memgraph 文件的概览。

介绍报文中一些需要关注的类型:
__TEXT 代码段,只读,包含函数,和只读的字符串
__DATA 数据段,读写,包括可读写的全局变量等
__LINKEDIT 包含了方法和变量的元数据(位置,偏移量),以及代码签名等信息
__OBJC 如果应用程序包含 Objective-C 代码,则此区域包含 Objective-C 运行时库代码
Shared Memory 与其他应用程序之间共享的系统库(例如 Cocoa 和 OpenGL)
Mapped file 该区域包含经常被访问的文件内容,并映射到虚拟内存以支持更快的访问
Stack 包含栈存储器,包括每个函数调用的参数
__TEXT,__DATA,__LINKEDIT 这三个都对应 Mach-O File 的对应字段。关于更多 Mach-O 文件的分析,参照笔者的 深入理解 iOS 启动流程和优化技巧 - mach-O
vmmap --verbose
我们在 .memgraph 文件概览中只看到 Stack 大小, 我们在内存管理分配时所说的 Heap 存储,从哪里看到呢?
我们在这里先简单说明一下 OS X / iOS 的分配基础。
OS X 的 Heap 内存分配存在一种可拓展的机制。通过这种机制内存分配根据大小分为 3 种类型(Tiny,Small,Large)。我们后面可能详细说明一下这种机制,用来解释一下我们应该怎么在内存中分配一个大的内存或者多个连续或不连续内存。
在.memgraph 文件 中我们可以查看 MALLOC_LARGE,MALLOC_SMALL 和 MALLOC_TINY 这三个来了解 Heap的分配。
可以使用这个命令来查看
vmmap --verbose test.memgraph | grep "MALLOC_TINY"
获取 Dirty Memory 页数
使用下述获取方法获取内存中Dirty Memory 的页数
vmmap -pages test.memgraph | grep '.dylib' | awk '{ sum += $6} END { print "Total Dirty Pages:" sum } '
LEAKS
显示已分配但不再引用的对象,并显示泄漏所属的引用循环。
leaks a.memgraph
使用 leaks test.memgraph 获取 leaks 的分析结果

打开 malloc 日志堆栈
建议开启 malloc 日志堆栈,从而获取到根节点的回溯。
在运营应用程序之前 Edit Scheme -> Run -> Diagnostics

使用 leaks test.memgraph 获取对应的描述信息

如何查询
通过命令打印的信息比较多,很容易被命令行工具进行截取,并且当我们想要对对某些内存进行深究时,往往需要花费很长的时间。
leaks -helper 获取 leaks 工具支持的方法

然后感觉 --trace 或 --traceTree 命令有所帮助

Heap
显示分配在进程堆上的对象,这些对象用于标识内存中的大的对象及其分配的对象。
heap -sortBySize a.memgraph
使用 heap -sortBySize a.memgraph 获取按照大小逆序排列的所有分配在堆中的对象

使用 heap 命令可以使你对分配的对象一目了然。
实践价值
如果我们的项目工程里使用了大量的自定义名称的类名,我们可以通过使用 heap 命令来获取当前分类所创建的超过某个代码限制的类
通过下面的命令可以获取到当前有多少超过 100 字节以上的对象地址。
heap --addresses=UIImage[100-] test.memgraph
一个过大的内存创建意味着可能有些问题。如果我们自定义的类占据了过多的内存和创建过多次数,可能意味着有些问题
除了运行过程中的内存分配,如果我们在首屏加载完成以后,立马导出我们的 .memgraph 文件,会不会有助于我们分析有哪些类在不必要的时候创建了?
MALLOC_HISTORY
对待已经开启 malloc 日志堆栈所生成的.memgraph 文件,我们可以通过使用 malloc_history 命令来做点有趣的事情。
前文说过,打开 malloc 日志堆栈 有助于我们进行回溯。当我们对一个地址心存疑惑,或者想知道他是什么时候创建的, 使用这个命令为内存中的给定地址提供回溯,从回溯中,可以识别并更好地管理导致大量内存分配的函数/类。

总结
在实际项目中,我们如何取舍这种分析方法呢?
| 内存创建(Creation) | 引用(Reference) | 具体大小(Size) |
|---|---|---|
| malloc_history | leaks | vmmap, heap |
当我们需要进一步了解如何介绍内存占用时,可以尝试使用上述工具
Use memory graphs to further understand and reduce memory footprint
参考文档
iOS — Advanced Memory Debugging to the Masses
iOS Memory Deep Dive
文章每周持续更新,欢迎关注「 iOS 成长指北 」第一时间阅读催更。一起学习,获得成长。如果本文对你有所帮助,欢迎点赞、分享、收藏
274




被折叠的 条评论
为什么被折叠?



