内存监控模块
内存监控用于监控和衡量 App 在线上的内存使用情况。内存作为设备中的重要资源,与系统内的各种进程共享使用。按照 iOS 系统的策略,在前台过度使用内存面临被系统直接杀死的情况;在后台占用大量的内存资源,也会致使 App 被系统杀死以回收资源给前台 App 使用。
鉴于此,在合理有效的使用内存资源尤为重要。因此 Bugly 针对提供了内存监控模块,帮助业务衡量 App 在线上的内存使用情况,并体统必要的诊断信息,以定位和优化部分内存问题。
为了实现内存的有效监控,主要提供了如下监控内容:
内存指标
内存指标从整体角度来衡量 App 的内存使用情况,主要包含两个指标:
前后台内存峰值
峰值指 App 在一次进程生命周期中所使用的物理内存(phys_footprint
)的最大值。峰值内存越高,意味着 App 在极端情况下使用的内存越多。如前所述,在前台活跃时,越高的峰值意味着其触碰到内存限制的概率越大;同理在后台时,越高的峰值意味着 App 被系统杀死的概率越大。
因此将峰值指标以前后台为区分,定义为前台内存峰值与后台内存峰值,顾名思义,其分别表示 App 在前台的内存与后他内存的峰值。
- 前台内存峰值:其值越高,意味着发生 FOOM 的概率越高,一般情况下,其增长趋势(尤其是 P99 )与 FOOM 率呈正相关;
- 后台内存峰值:Bugly 收集到 App 退后台时,最后的内存值。后台内存高,意味着 App 在后台被系统回收的概率越高,因此业务有必要优化 App 在退后台后的内存使用情况。由于 Bugly 是进程内收集的指标,因此其值与系统真是值而言,相对偏高,更多为参考价值;
峰值指标与其他指标类似,支持以不同的时间粒度和筛选条件进行查询对比,具体操作可以参照《指标接入指引》。
FOOM 率
FOOM (Foreground Out Of Memory),前台内存溢出:我们将 App 在前台触发到系统内存限制而被 SIGKILL 信号杀死的情况,定义为 FOOM。
由于 FOOM 是被系统 SIGKILL 信号杀死的闪退,不同于一般的异常引发的 Crash ---- 是进程内逻辑执行触发的异常导致,因此传统意义上的 Crash 率并未包含此部分退出情况。同时,由于进程内部无法直接捕获到 SIGKILL 信号,因此无法直接记录到准确的 FOOM 退出事件。
在 Bugly 中,FOOM 事件通过收集 App 生命周期中的各类状态,例如是否在前台/后他,是否发生已捕获异常,App 最后使用的内存等信息,综合推断是否发生 FOOM 事件。因此,FOOM 的判定结果,可能包含一部分误判定的数据(误判指判别的退出原因不正确,但一定是非正常的退出。)。
在后续实践过程中发现,iOS系统不仅在内存使用过高时会杀死APP,当设备过热、CPU调度使用频繁、触发过多的IO等情况,都会杀死APP。
FOOM 率即为 FOOM 发生次数与进程启动次数之间的比值,衡量 FOOM 发生的频率。同时加入了从设备角度和用户角度衡量的指标,即设备 FOOM 率和用户 FOOM 率,二者均是有设备 ID 和用户 ID 去重后计算所得。
虽然包含一定的误判,但其整体依然能衡量 App 因内存过度使用,在前台被系统杀死的严重程度。
内存诊断信息
为了帮助业务在优化上述指标,Bugly 在提供指标的同时,提供了相关的诊断信息。诊断信息的主要目的是提供主要内存使用的情况,以此提供给业务分析导致内存过度使用主要原因,以确定如何优化。
内存使用问题与一般的 Crash 问题不同,Bugly 收集提供的数据并不是准确导致 FOOM 发生的原因。
一般 Crash 中,发生问题是程序执行到某一个点遇到异常,因此对于捕获到的调用栈和现场数据就是发生异常的点,分析解决就可以。
FOOM 问题的本质是内存资源的过度使用导致,因此仅凭某一次内存分配的记录不能说明其内存使用就一定是不合理的,需要结合 App 中所有的内存分配,找到哪些业务逻辑使用了大量且不合理的内存,再结合其业务需求进行优化。
也是出于这个原因,FOOM 内存问题需要收集更多的信息才能帮助到业务,进而导致了其监控逻辑自身的性能开销。为了避免监控逻辑自身开销影响正常的业务逻辑,Bugly 在内存诊断数据的采集进来抽样处理(默认1%),故此 FOOM 个例信息在绝大部分个例中是无详细信息的。
Bugly 提供的内存资源使用监控能力,主要分为一下几点能力:
FOOM 个例
FOOM 个例即 SDK 检查到为 FOOM 退出事件后,上报的相关详细信息。对于解决 FOOM 个例问题而言,其提供的信息至关重要。关于 FOOM 个例详细信息和主要的分析方式,参考 FOOM 个例详情。
大内存分配
大内存分配作为一种辅助手段提供,其主要是监控 App 在运行过程中,异常(一次分配超过指定阈值,默认 10MB)的内存分配行为。故此,其上报数据更多为参考意义,业务需要关注此类分配是否合理,而非一定为异常问题。
在大内存分配中,Bugly 主要提供触发分配的调用栈和分配大小,其他信息则为相关的时间、设备等等信息。
与其他个例问题类似,Bugly 中将上报的数据,按照分配的调用栈进行聚类形成 issue,方便开发者分类跟进问题。
VC 泄漏监控
原称“VC内存泄漏”,因此在 Bugly 中部分地方还保留此名称,但其不够准确。
VC 泄漏,指 iOS App 中的 UIViewController 及其相关子对象在结束使用(pop, dismiss, remove)后,没有被释放的情况(默认延迟 30s 检查)。
由于 iOS 原生应用中,UIKit 框架将 UIViewController 作为组织 App 各个产品逻辑和页面的基础对象,因此 App 的主要逻辑基本都会围绕 UIViewController 展开。Bugly 会监控所有 UIViewController 的主要行为情况及其相关的指标数据变化。在此基础上,对于结束使用而未释放的 VC 对象进行了异常上报处理,以便业务关注此类 VC 对象是否合理释放,避免无用的持有导致占用大量资源。
在部分场景下,处于业务需求,可能会持有部分 VC 对象不释放。Bugly 无法甄别此类情况,因此依然会做上报。若业务不期望此类问题再做上报,可以通过 SDK 配置使用指引添加白名单实现过滤。
与大内存分配类似,VC 泄漏除基础信息外,主要提供 VC 的创建调用栈和名称。
需要特殊说明的是,在 VC 泄漏个例中“泄漏内存”的值。该值是取对应泄漏 VC pop(dismiss, remove) 时的 phys_footprint
值与其创建时的值的差,若为负数则取 0。故此其为一个参考值,并非真实的内存泄漏值。在使用过程中,更多应关注 VC 是否合理持有,不必过于纠结泄漏内存的值。
内存详情(内存图)
内存详情主要目的是给开发者提供更为详尽的内存使用情况。目前在内存详情中,主要提供内存图个例。内存图包含 App 某一时刻的内存分配和使用信息,及其相关的节点之间的引用关系、主要分配堆栈等信息。详情参考内存图。