最近在看FOOM,翻译一下facebook的解决方案。
原文链接在Facebook iOS app中减少FOOMs
在Facebook iOS app中减少FOOMs
在Facebook,我们致力于使我们的应用程序稳定、快速和可靠。我们一直在努力减少Facebook IOS应用中的崩溃次数,并提高其整体的可靠性。过去,大多数crash是由于程序错误引起的,并且它们总是带有可以定位错误的堆栈和一直提供crash根源的提示。
随着我们不断修复Crash问题,我们观察到Crash率不断下降,但是从App Store的评论中我们注意到社区仍然对应用crash感到崩溃。我们深入研究了用户报告,和开始推断可能发生了OOM(out of memory)。当系统内存不足时,操作系统会终止应用程序以回收内存时,会发生OOM。无论应用程序在前台还是在后台,都可能发生OOM。我们在内部分别将它们称为FOOMs和BOOMs——可以说应用程序发生BOOM是有点有趣的。
从用户的角度来看,前台OOM(FOOM)与Crash是无法区分的。在这两种情况下,应用都会意外终止,出现消失和用户被带回到设备的主屏幕。如果内存消耗率急剧增加,则应用程序被杀死而不会收到任何有关内存用尽的信号。在IOS上,操作系统会尽力向应用程序发送内存警告,但不能保证在操作系统退出进程之前始终收到警告。这使我们无法轻松知道该应用由于内存不足而被操作系统杀死。
处理问题
为了了解我们的应用由于OOM Crash而闪退的频率,我们首先枚举了可以终止应用的所有已知路径,然后对其进行记录。我们研究的问题是“什么导致应用程序启动?”
应用可能由于以下原因需要启动:
- 程序是否升级
- 程序是否调用exit()和abort()
- 程序是否crash
- 用户向上滑动强制退出应用程序
- 设备重启(包括操作系统升级)
- 程序在后台或前台内存不足(OOM)
通过排除法,寻找不属于其他情况的实例,然后我们可以确定何时发生OOM。同时我们也跟踪应用程序是否在前台还是后台,以便我们可以准确的将OOM分解为BOOMs和FOOMs。
日志记录表明,内存较少的设备上的OOM发生率较高,这是可以预期的并且令人放心的,因为应用程序更有可能在受限内存的设备上退出。看到日志记录中的相关性有助于我们验证排除的实例是否正确并继续改进日志(我们最初并未识别所有情况,例如应用程序升级)。
我们减少OOM的数量的第一个尝试是尽可能回收应用程序不在需要的内存。不幸的是,我们观察到OOM崩溃的次数并没有变化。因此我们将重点转移到了大的内存分配上。首先是可能泄漏的内存(未清理过),尤其是通过潜在的保留周期。
分析内存使用情况
当我们开始修复内存泄漏时,我们看到OOM率有所降低,但是我们仍然没有看到我们希望的显著降低。接下来,我们深入研究了Apple的Instruments 应用程序中的内存分析器和注意到一旦应用程序打开任何页面,UIWebView就会重复分配大量内存。我们还发现,即使用户关闭web视图离开页面后,也常常无法回收内存。
我们尝试了许多优化措施,例如清理缓存和清理内容,但是导航到Web视图后,我们应用程序进程的内存占用始终显著增加。iOS 8引入了一个新类WKWebView,该类实际上是在一个单独的进程中执行其大部分工作,这意味着大多数与Web视图相关的内存使用情况不会统计到我们的进程。在内存不足的情况下,web视图的进程可能会被杀死,而我们的应用程序更有可能继续运行。在将应用程序迁移到WKWebView之后,我们确实看到了应用程序中的OOM的显著减少。Yay!
内存分配率
通过Instruments进行内存使用情况分析时,我们还观察到应用程序使用内存情况的某些实例,在这些实例中,应用程序临时分配了大量内存(约30M),不久后释放。如果在此分配过程中CPU不空闲,则OS可能会杀死该应用程序。如果我们能够摆脱这些临时分配,这有助于在某些情况下将OOM减少多达30%。我们还进行了试验和发现一次性分配并保留到内存中对于提高应用程序的稳定性要比重复分配和释放更好。
阻止回归
即使迁移到WKWebView之后,我们仍然发现较小的内存泄漏也可能会明显影响OOM率,尤其是在内存受限的设备上。由于我们频繁的发布计划,并且有许多团队贡献代码到应用程序中。因此捕获并防止我们发布的应用程序内存泄漏是重要的。我们利用了最初用于测试移动性能的 CT-Scan infrastructure来记录进程中的常驻内存量(测试左移),允许CT-Scan在代码被引进的时候就对回归量(内存)进行标记。这会帮助我们在开始工作时将OOM率保持较低水平。
内置的APP内存分析器
我们在工程中使用的最后一个关键策略是构造一个内置的App内存分析器。该分析器通过追踪所有Objective-C对象的分配来快速对应用程序进行性能分析。我们在CT-Scan和应用程序的内部版本中进行了配置。
它的工作原理如下:对于系统中的每个类,它会计数当前有多少个存活实例。我们可以随时查询它,并记录每个类的当前对象数。然后,我们可以分析此数据的每个发布版本以识别应用程序整体分配模式的变化,通常当计数急剧变化时可以帮助我们识别是否内存泄漏。我们设计了一种足够有效的方法来实现,从而不会对用户的应用程序性能产生任何明显的影响。
这是我们的策略以及如何跟踪NSObject分配的示意图。
我们首先创建了一个内存分配跟踪器类。它非常简单,该跟踪器会将类名和实例计数器进行映射,以及增加和减少计数的公共方法。我们选择使用C++而不是Objective-C,以便将跟踪器的所有内存分配和Cpu开销保持在最低水平。
class AllocationTracker {static AllocationTracker* tracker();void incrementInstanceCountForClass(Class aCls);void decrementInstanceCountForClass(Class aCls);std::vector<std::pair<Class, unsigned long long>> countsSnapshot();...
}
然后我们使用ios方法替换(叫做“swizzling,” 使用运行时函数class_replaceMethod)将标准的IOS方法+alloc 和 +dealloc替换为方法–fb_originalAlloc 和 –fb_originalDealloc.
然后,我们用新的实现替换
+alloc 和 +dealloc。这些实现分别会增加和减少分配和释放的实例数量
@implementation NSObject (AllocationTracker)+ (id)fb_newAlloc
{id object = [self fb_originalAlloc];AllocationTracker::tracker()->incrementInstanceCountForClass([object class]);return object;
}- (void)fb_newDealloc
{AllocationTracker::tracker()->decrementInstanceCountForClass([object class]);[self fb_originalDealloc];
}@end
然后,当应用程序运行时,我们可以定期调用快照方法以记录当前存活实例数量
App稳定性很重要
当我们推出了解决Facebook iOS应用程序中内存问题的方法时,我们发现(F)OOMs以及应用程序crash的用户报告数量都大大减少了。OOM崩溃对我们是一个盲点,因为没有正式的系统或API可以观察OOM事件及其发生的频率。当应用突然闪退时,没有人喜欢它。借助一些工具,向最新的iOS技术迁移,以及在最开始的时候就尝试解决这个问题(测试左移)也是可以借鉴的,我们能够使我们的应用程序更加可靠,并确保在打开Web视图时不会突然关闭应用程序。
还要感谢Linji Yang,Anoop Chaurasiya,Flynn Heiss,Parthiv Patel,Justin Pasqualini,Cloud Xu,Gautham Badrinathan,Ari Grant和其他许多人,他们帮助降低了FOOM率。
副业赚钱
说完了正文,接下来打一波广告,程序员如何副业赚钱,我本科同学这里有一个赚钱的机会,现在是北大金融系硕士,有副业赚钱想法的兄弟们,请添加他微信 zhengxiangpku
备注lfdanding推荐
即可。