OOM (out of memory) ,根据Facebook的这篇文章,分为FOOM( foreground OOM )和BOOM ( background OOM )。从用户角度来看,FOOM与通常crash无异。可是因OOM致使的crash没法被app捕获,这就为咱们断定OOM带来了难度。ios
要判断是否发生了OOM,首先想到的就是didReceiveMemoryWarning。git
咱们知道,当内存不足时系统会给app发送didReceiveMemoryWarning的警告。可是具体是app用了多少内存之后会发送这个警告呢?发送了警告以后是否是app就crash了呢?OOM致使crash前,app是否必定会收到这个警告呢?github
根据官方文档的说明,若是系统可用内存太低,又不能经过杀掉挂起的app来释放内存的时候,UIKit就会给正在运行的app发送一个low-memory警告。这里并无提到内存上限的大小。bash
实际上,根据Jetsam机制,OOM发生时,在系统强杀App前,会判断优线程先级,按照优先级去释放优先级低还使用内存多的线程。这个优先级规定是:内核用线程的优先级是最高的,操做系统的优先级其次,App 的优先级排在最后。而且,前台 App 程序的优先级是高于后台运行 App 的。 因此若是OOM发生的时候,你的app若是在后台也有可能被强杀。若是你的app在前台,能使用内存上限也不是一个固定值。微信
实际上,若是收到内存警告后,内存并无快速持续上涨,咱们又很是快速的释放了内存,app并不会crash。也就是说只要及时地下降内存的使用,app就不会crash。app
咱们能够实际测试一下:async
在主线程执行下面的代码:测试
while (true) {
NSData *bigData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"some_image" withExtension:@"png"]];
[oom addObject:[bigData copy]];
}
复制代码
bigData
足够大的状况下,好比几十k,app很快crash,而且没有收到低内存警告。由于这时,内存增加很快,主线程又忙。ui
那若是在其余线程执行上述代码呢?spa
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (true) {
NSData *bigData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"some_image" withExtension:@"png"]];
[oom addObject:[bigData copy]];
}
});
复制代码
这时,app收到低内存警告了。也就是说,虽然都是急速的上涨内存,若是主线程不那么忙,就能收到低内存警告。
因此,发生OOM时,并不必定能收到低内存警告。Facebook的这篇文章也说并非总能收到内存警告:
If the rate of memory consumption increases drastically, the application can be killed without receiving any signal that memory is running out. On iOS, the OS does its best to send a memory warning to the app, but there is no guarantee that one will always be received before the OS evicts the process from memory.
我想咱们证实了这个观点。
经过上面的论述,咱们知道:收到低内存警告不必定会crash,OOM时也不必定能收到低内存警告。
Facebook的Reducing FOOMs in the Facebook iOS app提到,他们是经过在app启动时,排除启动缘由来找到OOM的:
在app启动时,咱们依次判断:
这个项目是上述流程的实现。
可是这个模型并不完美。其中最大的漏洞就是那些不能被捕获的crash。
不能被app捕获的crash通常都是由于系统强杀引发的,OOM是其中一种,但还有其余状况。
其余被系统强杀的缘由最多见有两种,一是后台任务超时和二是主线程长时间卡顿被watchdog强杀,也就是常见的0x8badf00d。固然,除了这两种状况还有其余被系统强杀的缘由,好比电池过热,启动时间过长等。
随着iOS系统的升级,系统强杀的种类和阈值可能都会发生变化。
因此,这种排除法有必定误报的可能。
排除法是facebook采用的方法,微信的Matrix应该也采用了这个方法。可是,排除法会有必定误报的可能,实现难度也更高。
从收到内存警告到app crash,中间有几秒的时间,一种说法是6秒,但我实测是4秒左右,系统的日志也是在这段时间产生的。若是app收到了低内存警告,又在几秒钟以内crash了,基本上就能够100%肯定发生了OOM。
对于收不到低内存警告的OOM,咱们就无能为力了。但我认为这种状况是少见的极端状况。
因此两种断定OOM的方法都不完美,综合实现难度来看,利用内存警告判定是否发生了OOM,也许是一个比较不错的选择。