当前位置: 首页 > 科技观察

Facebook如何减少应用程序中的FOOMs

时间:2023-03-14 19:30:16 科技观察

在Facebook,我们一直在努力使我们的应用程序稳定、快速和可靠。在FacebookiOS应用上,我们做了很多工作来减少应用崩溃并提高应用的整体稳定性。以前,大多数崩溃都是由例行错误引起的,通常伴随着相应代码行的堆栈回溯,并提供有关可能导致问题的原因的提示。随着我们继续解决崩溃问题,我们观察到需要解决的崩溃问题的百分比正在下降,但我们注意到AppStore表明社区继续遇到令人失望的应用程序崩溃问题。我们深入研究了用户报告,并推测内存不足事件(OOM)可能正在发生。OOM一般发生在系统运行在低内存环境下,OS为了回收内存而终止应用程序。它可以在前台和后台发生。我们在内部称它们为FOOMs和BOOMs——当我们说应用BOOMs时有点滑稽。从用户的角度来看,很难区分前台内存不足崩溃和常规崩溃。一般分为几种情况,应用程序异常终止,出现消失,用户返回到设备主屏幕。如果内存消耗率快速增加,应用程序将在没有任何通知的情况下终止。在iOS中,操作系统会向应用程序发送内存警告,但不保证操作系统会在终止应用程序之前向应用程序发送警告消息。这使我们无法轻易知道应用程序是否由于内存压力而被操作系统终止。问题分析为了掌握应用程序因OOM崩溃而终止的频率,我们从所有已知的方式中列举了应用程序可能终止的情况并记录下来。然后问题变成“是什么导致应用程序重新启动?”应用需要重启的原因有:应用已更新应用退出或终止应用崩溃用户强制退出应用设备重启(包括操作系统升级)应用在前台或后台Outofmemory(OOM))通过排除处理,寻找与其他重启原因不同的实例,所以我们可以找出OOM发生的时间。此外,我们还跟踪应用程序何时进入后台和前台,因此我们可以准确地将OOM分为BOOM和FOOM。当设备处于低内存状态时,日志显示OOM率很高。当应用进程在内存受限的设备上像被逐出一样被杀死时,这真的很令人沮丧。查看相关日志有助于我们验证排除方法的效果,并可以继续改进日志(我们无法准确验证所有情况,例如应用程序升级)。我们最初减少OOM的努力是尝试在应用不再需要内存时尽快积极缩小应用的内存占用。不幸的是,我们没有看到OOM崩溃次数有明显变化,因此我们将注意力转移到大内存分配上,并开始查看可能泄漏(未清理)的内存,尤其是潜在的循环引用。内存使用分析当我们开始修复内存泄漏时,我们看到OOM崩溃率有所下降,但仍未达到我们的预期。接下来,我们深入研究了Apple的Instruments应用程序的内存分析器,注意到每当该应用程序打开任何网页时,重复样式的UIWebView都会分配大量内存。我们还发现内存通常不会被回收,即使当用户离开页面并关闭webview时也是如此。我们尝试做了很多优化,比如清理缓存和内容,但是在跳转到webview时,应用进程的内存占用总是显着增加。iOS包含一个新类——WKWebView——它将大部分工作放在一个单独的进程中,这意味着大多数与内存相关的Web视图使用不会分配给我们的进程。在内存不足的情况下,Web视图的进程将终止,但我们的应用程序很可能会存活下来。在我们将我们的应用程序迁移到WKWebView之后,我们确实看到了OOM发生率的显着降低。耶!内存分配率当通过Instruments分析内存使用情况时,我们还发现该应用程序分配了大量临时内存(~30MB),然后立即将其释放。如果在此分配期间CPU空闲,操作系统将终止程序。我们想禁止这样的临时分配,这可以帮助我们减少30%确定性场景下的OOM崩溃,我们也进行了实验,发现分配一次然后管理内存比重复分配和释放内存对应用程序可靠性更好。防止内存损坏即使使用WKWebView,我们仍然发现即使是一点点内存泄漏也会显着影响OOM发生率。在我们通常的发布计划以及为应用程序做出贡献的许多团队中,捕获和防止已发布应用程序中的内存泄漏非常重要。我们更改了专门用于测试移动性能的扫描设备,让扫描设备在添加损坏案例时立即对其进行标记,以记录大量进程的常驻内存。这帮助我们将OOM发生率保持在比我们最初解决问题时低得多的水平。In-ApplicationMemoryProfiler我们在这个项目中使用的最后一个关键技术是构建一个in-appmemoryprofiler,它可以通过跟踪所有Objective-C对象的内存分配来快速分析应用程序。我们在扫描仪上配置它,并在其中构建我们的应用程序。工作原理:对于系统中的每个类,维护当前活动实例的计数。我们可以随时要求它打印出存在的每个类的对象数。然后,我们可以分析此数据以了解任何异常的发布到发布,以确定我们应用程序中的整体内存分配模式,如果计数发生剧烈变化,通常可以将其验证为内存泄漏。我们将以一种足够高效且不会对用户产生影响的方式来实施它。下面是对我们的策略以及我们如何跟踪NSObject内存分配的简要描述。我们首先创建一个内存分配跟踪类。这是一个超级直接和简单的类,有一个公共实例计数方法来计算实例的增加和减少。我们使用C++而不是Objective-C,因为它可以最大限度地减少跟踪器内存分配和CPU使用率。classAllocationTracker{staticAllocationTracker*tracker();voidincrementInstanceCountForClass(ClassaCls);voiddecrementInstanceCountForClass(ClassaCls);std::vectorcountsSnapshot();...}然后我们可以使用iOS方法部署技术(称为“调配”,使用运行时的class_replaceMethod方法),使用-fb_originalAlloc和-fb_originalDealloc方法来替换标准的iOS方法+alloc和+dealloc。然后,我们将+alloc和+dealloc分别替换为新实现的增加和减少分配和释放实例数量的方法。@implementationNSObject(AllocationTracker)+(id)fb_newAlloc{idobject=[selffb_originalAlloc];AllocationTracker::tracker()->incrementInstanceCountForClass([objectclass]);returnobject;}-(void)fb_newDealloc{AllocationTracker::tracker()->decrementInstanceCountClassFor([objectclass]);[selffb_originalDealloc];}@end然后,当应用程序运行时,我们可以调用快照方法定期打印存活实例数。应用程序可靠性在我们对Facebook的iOS应用程序实施更改以解决内存问题后,我们发现用户的(F)OOM和应用程序崩溃报告显着减少。OOM崩溃对我们来说是一个盲点,因为没有正式的系统或API可以随意检测它们。没有人喜欢突然关闭的应用程序。但是使用一些工具,或者最新的iOS技术,以及一些巧妙的方法来解决这个问题可以使我们的应用程序更加可靠,并确保您不会打开Web视图来查看有趣的文章(比如您正在阅读这篇文章)突然关闭。还要感谢LinjiYang、AnoopChaurasiya、FlynnHeiss、ParthivPatel、JustinPasqualini、CloudXu、GauthamBadrinathan、AriGrant和许多其他帮助降低FOOM率的人。