虽然大部分问题在开发中就可以避免,但是有些隐蔽的问题还是需要一些工具来监测
Xcode自带的工具是测试阶段的首选,这里就不讲了。
内存泄漏
这是一个很容易导致 OOM 的问题,在 App 中内存泄漏大部分是由于循环引用引起的。
我们需要在对象应该被释放之前,传递给一个延时执行的方法,来判断一段时间后对象是否有被销毁:
1 | public class MemoryChecker { |
控制器是一个很重的对象,如果控制器退出没有释放,那么会造成很大的内存泄漏,所以在监测 NavigationController
的 pop
与 deinit
方法以及 ViewController
的 dismiss
方法:
1 | class NavigationController: UINavigationController { |
也可以覆盖 UITableView,监测 TableView 释放后, cell 是否有内存泄漏
1 |
|
当然有时候控制器会释放,但是它持有的某些 View 内存泄漏,所以实际应用中会进行递归遍历控制器与view的所有强引用变量。
第三方库
MLeaksFinder
MLeaksFinder
就是通过类似上面逻辑来判断是否有内存泄漏,它会重载 NSObject
的 willDealloc
方法,对需要检测的对象进行延时 2 秒后检测,如果还存在,那么就打印对应的持有栈信息:
1 | - (BOOL)willDealloc { |
MLeaksFinder
对代码没有入侵,只要导入头文件即可,是因为它使用了Category
进行一部分方法的重写以及使用方法替换了 UINavigationController
中的一系列 pop push
方法与 UIViewController
中的 dismiss viewDidDisappear
等
缺点是对于 Swift 类无法监测
FBRetainCycleDetector
这是用来检测循环引用的,将想要检测的对象传入探测器,会对对象的强引用对象进行递归遍历,默认查找10层,最后在整个有向图中应用 DFS 算法查找环。如果有向图存在环,则说明目标对象存在循环引用。
MLeaksFinder
中也集成了 FBRetainCycleDetector
,在检测到内存泄漏后,可以直接点击 Retain Cycle
将检测到泄漏的对象提交给 FBRetainCycleDetector
监测是否有循环引用
OOM监测
由OOM引起的崩溃程序无法捕获,也就没办法统计反馈。
2015年 facebook 提出了一种方案,在程序启动时,根据上一次程序终止保存的状态,排除:
- 该应用程序已升级。
- 应用程序调用退出或中止。
- 该应用程序崩溃了。
- 用户向上滑动以强制退出应用程序。
- 设备重新启动(包括操作系统升级)。
这些问题,那么就判定为发生了 OOM,再根据App最后是否在后台判断是 FOOM 还是 BOOM
但是这种判断没法检测所有场景,容易发生误判。
OOMDetector
腾讯开源的工具,通过Hook IOS系统底层内存分配与释放的方法,跟踪并记录进程中每个对象内存的分配信息,包括分配堆栈、累计分配次数、累计分配内存等,这些信息也会被缓存到进程内存中。在内存触顶的时候,组件会定时Dump这些堆栈信息到本地磁盘,这样如果程序爆内存了,就可以将爆内存前Dump的堆栈数据上报到后台服务器进行分析。
虽然此工具性能高,但是持续的监控内存还是会 cpu 性能、内存占用造成一定的影响。
字节跳动的方案
定时监测 App 内存占用,超过设置的阈值后启动内存分析,挂起所有其他线程,对内存进行快照,采集所有节点信息,然后构建引用关系,生成日志后恢复所有线程状态,然后对日志进行压缩 上传处理,因为在采集的时候会挂起所有非采集线程,所以整个 App 会卡住,需要限制触发频率与上限。
好处是在程序正常运行时,几乎不会对性能产生影响,只有在临近 OOM 时,会进行内存信息采集。
最后
由于 OOM 无法直接捕获,只能通过这些间接的方法来实现,具体上线项目使用什么方案,按项目情况自己选择。