作者 | 王海超
直播 OOM 问题比较棘手难以定位,主要体现在涉及的业务很多,从定位到解决花费时间比较久。为了提前触达问题,提高定位的效率,也是对现有工具的补充,提出直播内存抖动解决方案- MemoryThrashing。
为什么要提出这个方案 ?
维基百科中 thrashing 定义:
从业务视角定义内存 thrashing:
通俗讲就是性能数据有大的波动,拿内存来讲,当内存短时间从 600M 涨到 800M 叫作一个抖动。希望通过自研工具找出这 200 M 内存增长来自于哪里,在实际的 OOM 案例中因内存突增导致的 OOM 是比较常见的,具体现象如下:
以临时对象、内存堆积为例来阐述如何定位该类问题,通过“AllocTime Summary” 描述临时对象分配次数,通过 “Memory Summary” 描述内存堆积。
临时对象:短时间分配大量对象,导致直播稳定性波动较大,可能使内存、CPU 负载变高。这类问题通常表现为短时间内存冲高或者直接 OOM,或之后开始迅速回落到正常水位,这类对象不会驻留内存过久,通过监控 “临时对象” 可以提前发现这类问题。
以上是按分配次数(AllocTime Summary)统计的 TOP 临时对象,“AllocTime Summary 1” 代表第一次采样 Class 的分配次数其它依次类推。举例:通过 diff “AllocTime Summary 2” 与 “AllocTime Summary 1” 差值可知 “LivexxxA” 在采样周期分配了 7803 次,由于未采集到 “Memory Summary” 信息,可认为未有内存驻留。
内存堆积:内存驻留了大量对象,而且这类对象短时间不会释放掉,导致内存水位居高不下,很容易触发 OOM 问题。
以上是按内存驻留统计的 TOP 实例,“Memory Summary 1” 代表第一次采样实例数量的内存驻留信息其他依次类推。举例:通过 diff “Memory Summary 2” 与 “Memory Summary 1” 可知 “LivexxxA” 在采样周期内增长了 56791 个,根据最后一次采样可知内存驻留了总共 69904 个实例,通过采样可知“LivexxxA” 每次都是递增的。
方案思路是做内存差值找出增长,通过采样多个时刻的内存信息(目前主要监控 Class 的实例个数), Diff 出内存信息找出 TOP 增长,达到归因的目的。
通过内存节点遍历与已注册的 Class 比较统计实例个数,该方案的优点是可以监控整个 APP 的 OC 对象实例个数,面对直播业务场景需不需监控整个 APP 的对象,目前看暂时用不到,需求出发点是监控直播场景且满足一定条件。比如:直播观播一段时间后内存的大幅波动,场景比较聚焦。另一个考虑是如果当前内存比较大,遍历 zone 会比较耗时,如果不挂起线程会有潜在的崩溃问题、以及数据不准问题。
通过 Hook 的方式,统计 Class 实例的分配、释放次数,达到记录实例存活个数的目的,可监控固定场景的 OC 实例增长情况,如直播间内的内存突增,范围比较小不需要统计过多的无用对象。该方案相对内存区遍历耗时小,且不会有野指针问题。但需要注意的是监控对象时对性能的影响,目前采用的是 RunTime 方案,从线下直播间测试情况看对主线程的影响忽略不计。
在实际开发过程中发现对象的创建、释放处于复杂的多线程环境中,处理不当会对业务产生潜在的影响,影响到业务执行效率或者造成稳定性问题:
经过优化采用多级缓存方案解决主线程的性能开销问题,达到主线程几乎零开销。
在进入直播间一段时间后开启监控,通过监控内存值变化来区分是否开启采样功能,开启采样后会进入连续多次采样阶段,多次采样完成后进行数据上报,上报完成后会继续监控内存。
在高热直播间多次采样的内存快照,采集 TOP 100 数据,以 “LivexxxA” 为例两次采样中第二次增长了 4125 个实例,可以简单归因 “LivexxxA” 相关业务导致 “MemoryThrashing”,可以从 “LivexxxA” 相关业务入手排查。
方案 | 优点 | 缺点 |
“MemoryThrashing” | 可以多次采样,对比内存增长趋势;性能开销小,可线上全量;提前感知内存问题;上手简单,通过对象数量就可以排查问题; | 不支持多语言,只限于 oc 语言;不具备通过内存节点关系分析内存泄漏问题,只能找出堆积的对象;不具备分析多个内存区的能力;Hook 方式影响方法缓存; |
“MemoryGraph” | 问题发现能力强:可以通过内存节点关系分析内存泄漏导致的 OOM 问题;可以统计内存区的内存占用情况;适用多语言;上手复杂,需要梳理内存节点引用关系; | 线程挂起会影响业务执行,用户感知明显;内存使用越高,内存区遍历越耗时;只能少量采样; |
目前 “MemoryThrashing” 已经部署了,可以监控测试环境,后续将部署到线上。通过线下看提前暴露了很多问题,相对以往方式只有问题发生了或者产生了明显影响才能感知到,需要 QA 反馈到 RD,通过“MemoryThrashing”大大提升了排查效率,很好的将劣化问题前置发现,以下抽取其中两个案例。
如下,多个采样周期内出现了大量对象的分配问题,且这些对象未释放,并且导致了内存明显上涨,采样周期 3 比采样周期 2 多分配了 234024 个对象,且最后内存驻留了 238800 个 “LivexxxBigDataRead” 对象,占用内存 10.9M。
如下,是开播场景抓到的问题,在主播端开启弹幕狂欢时,过 Effect 认出人脸后,就会创建一个对应的轮廓模型给到中台去画轮廓,频率会很高,每 5 秒周期(实际时间更小)临时对象增量高峰可到 6w 个(后两次采样差值),由于未生成 “Memory Summary” 信息可认为未驻留内存 ,累计过百万次对象分配,对开播性能会产生直接影响:
只统计 OC 对象数据在某些情况下可能不够,比如公共基础对象异常增长,则没有办法追踪到具体成因,如果带有对象引用关系可以进一步锁定问题。当然这些都是对 “Memory Graph” 能力的补充,如果“Memory Graph” 已经抓到了数据,可以结合“Memory Graph” 锁定对象引用链路继而找到业务。
根据以往案例如:OOM、ANR 有不少会伴随着高 CPU 使用率,比如某次案例由大量数据处理导致的 OOM 问题,经排查发现负责该业务处理的线程 CPU 使用率很高,所以通过监控线程 CPU 使用率,来补充监控显得很有必要,可以通过线程名字、堆栈, 锁定怀疑的业务。
电话:400-123-4567
传 真:+86-123-4567
手 机:13800000000
邮 箱:admin@eyoucms.com
地 址:广东省广州市天河区88号