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,也許是一個比較不錯的選擇。