讓人懵逼的 iOS 系統內存分配問題

最近應用中出現低內存被殺的情況,所以就想辦法對造成這一個問題的根源進行定位,主要是兩個比較 Low 的思路

  • 線下使用 InstrumentsMLeaksFinder 來進行內存泄露的定位,后期可能考慮把測試階段的內存泄漏對象和泄漏位置上報到測試服務器進行數據分析,進而更精確定位問題
  • 線上繼續統計 Abort 率,然后對于出現內存警告、Abort 的情況,及時上報 App 的內存走勢,這個走勢包括 App 使用的物理內存大小,以及當前設備的整體內存使用情況。當然不會持續收集內存,我們會在 ViewController 初始化和銷毀的時候進行收集,這樣基本可以把問題定位到 Controller 級別。

讓人懵逼的地方就是如何獲取 app 的內存和設備的整體內存情況,在網上查了一下,答案千奇百怪,而且統計的結果差異也比較大,所以才有了此文

系統內存分配

據查閱 Apple 的官方文檔,操作系統的內存主要分為 Used MemoryFree MemoryUsed Memory 又可以分為Wired MemoryActive MemoryInactive Memory,同時提到了一個Purgeable Memory ,暫且把它歸類為 Active Memory 吧。

  • Free Memory:未使用的 RAM 容量,隨時可以被應用分配使用

  • Wired Memory:用來存放內核代碼和數據結構,它主要為內核服務,如負責網絡、文件系統之類的;對于應用、framework、一些用戶級別的軟件是沒辦法分配此內存的。但是應用程序也會對 Wired Memory 的分配有所影響。

  • Active Memory:活躍的內存,正在被使用或很短時間內被使用過

  • Inactive Memory:最近被使用過,但是目前處于不活躍狀態

    例如,如果您使用了郵件然后退出,則郵件曾經使用的 RAM 會標記為“不活躍”內存。“不活躍”內存可供其他應用軟件使用,就像“可用”內存一樣。但是,如果在其他應用軟件占用郵件的“不活躍”內存之前打開了郵件,郵件的打開速度會更快,因為其“不活躍”內存會轉換為“活躍”內存,而不是從較慢的驅動器進行載入。

  • Purgeable Memory:這個是查閱資料發現的,同時第三方庫中有統計此內存的大小,所以記錄一下。可以理解為可釋放的內存,主要是大對象或大內存塊才可以使用的內存,此內存會在內存緊張的時候自動釋放掉,一會可以查看 Demo 來驗證這一事實。

應用物理內存

這個就是理解上的物理內存,對于 app 的內存使用應該檢測這一數值的變化,而檢測虛擬內存的話意義不大,對于物理內存和虛擬內存在 iOS 上的分配可以查看這篇文章:先弄清楚這里的學問,再來談 iOS 內存管理與優化(一)。對于物理內存的用量檢測方法比較常見:(更新正確的獲取App內存占用的方法:http://ddrccw.github.io/2017/12/30/reverse-xcode-with-lldb-and-hopper-disassemblerhttp://www.samirchen.com/ios-app-memory-usage/

// 獲得當前 App 的內存占用情況
- (NSUInteger)getResidentMemory {
    struct task_basic_info t_info;
    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
    
    int r = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
    if (r == KERN_SUCCESS) {
        
        NSLog(@"resident_size %lu",t_info.resident_size / 1024/1024);
        
        return t_info.resident_size;
    }
    else {
        return -1;
    }
}

測試

我主要是在 app 中放了一個按鈕,每點擊一次分配一百兆物理內存,然后查看 app 的內存使用情況以及系統的內存分配情況,對于系統的內存分配查看方式,可以參考這里:iOS-System-Services/System Services/Utilities/SSMemoryInfo.m

在 app 低內存崩潰前,一直收集點擊按鈕后的內存變化,如下

2017-03-09 21:28:03.112138 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:03.112280 LDAPM[491:46357] free 549.734375
2017-03-09 21:28:03.112338 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:03.112361 LDAPM[491:46357] used 1226.390625
2017-03-09 21:28:03.112381 LDAPM[491:46357] active 740.156250
2017-03-09 21:28:03.112399 LDAPM[491:46357] inactive 291.109375
2017-03-09 21:28:03.112417 LDAPM[491:46357] wired 195.125000
2017-03-09 21:28:03.112544 LDAPM[491:46357] purgableMemory 11.515625
2017-03-09 21:28:03.112613 LDAPM[491:46357] resident_size 27

2017-03-09 21:28:36.326194 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:36.326284 LDAPM[491:46357] free 448.093750
2017-03-09 21:28:36.326307 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:36.326322 LDAPM[491:46357] used 1330.734375
2017-03-09 21:28:36.326340 LDAPM[491:46357] active 846.015625
2017-03-09 21:28:36.326358 LDAPM[491:46357] inactive 289.593750
2017-03-09 21:28:36.326377 LDAPM[491:46357] wired 195.125000
2017-03-09 21:28:36.326395 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:36.326450 LDAPM[491:46357] resident_size 134

2017-03-09 21:28:42.708484 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:42.708571 LDAPM[491:46357] free 352.718750
2017-03-09 21:28:42.708593 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:42.708607 LDAPM[491:46357] used 1427.968750
2017-03-09 21:28:42.708626 LDAPM[491:46357] active 944.703125
2017-03-09 21:28:42.708656 LDAPM[491:46357] inactive 289.203125
2017-03-09 21:28:42.708677 LDAPM[491:46357] wired 194.062500
2017-03-09 21:28:42.708695 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:42.708715 LDAPM[491:46357] resident_size 234

2017-03-09 21:28:46.940049 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:46.940146 LDAPM[491:46357] free 252.437500
2017-03-09 21:28:46.940166 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:46.940185 LDAPM[491:46357] used 1527.406250
2017-03-09 21:28:46.940203 LDAPM[491:46357] active 1043.875000
2017-03-09 21:28:46.940221 LDAPM[491:46357] inactive 289.484375
2017-03-09 21:28:46.940239 LDAPM[491:46357] wired 194.046875
2017-03-09 21:28:46.940256 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:46.940275 LDAPM[491:46357] resident_size 334

2017-03-09 21:28:49.930067 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:49.930154 LDAPM[491:46357] free 152.187500
2017-03-09 21:28:49.930177 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:49.930221 LDAPM[491:46357] used 1627.515625
2017-03-09 21:28:49.930248 LDAPM[491:46357] active 1143.921875
2017-03-09 21:28:49.930267 LDAPM[491:46357] inactive 289.515625
2017-03-09 21:28:49.930285 LDAPM[491:46357] wired 194.078125
2017-03-09 21:28:49.930303 LDAPM[491:46357] purgableMemory 12.593750
2017-03-09 21:28:49.930322 LDAPM[491:46357] resident_size 434

2017-03-09 21:28:52.780752 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:52.780830 LDAPM[491:46357] free 82.390625
2017-03-09 21:28:52.780852 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:52.780867 LDAPM[491:46357] used 1748.640625
2017-03-09 21:28:52.780918 LDAPM[491:46357] active 1078.281250
2017-03-09 21:28:52.780943 LDAPM[491:46357] inactive 479.546875
2017-03-09 21:28:52.780961 LDAPM[491:46357] wired 190.812500
2017-03-09 21:28:52.780979 LDAPM[491:46357] purgableMemory 12.562500
2017-03-09 21:28:52.780999 LDAPM[491:46357] resident_size 534

2017-03-09 21:28:55.454389 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:55.454456 LDAPM[491:46357] free 21.062500
2017-03-09 21:28:55.454476 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:55.454493 LDAPM[491:46357] used 1801.875000
2017-03-09 21:28:55.454511 LDAPM[491:46357] active 1070.968750
2017-03-09 21:28:55.454529 LDAPM[491:46357] inactive 540.312500
2017-03-09 21:28:55.454546 LDAPM[491:46357] wired 190.593750
2017-03-09 21:28:55.454564 LDAPM[491:46357] purgableMemory 10.906250
2017-03-09 21:28:55.454587 LDAPM[491:46357] resident_size 634

2017-03-09 21:28:58.757493 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:58.757688 LDAPM[491:46357] free 25.484375
2017-03-09 21:28:58.757710 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:58.757804 LDAPM[491:46357] used 1119.265625
2017-03-09 21:28:58.757830 LDAPM[491:46357] active 624.609375
2017-03-09 21:28:58.757848 LDAPM[491:46357] inactive 303.906250
2017-03-09 21:28:58.757866 LDAPM[491:46357] wired 190.796875
2017-03-09 21:28:58.757889 LDAPM[491:46357] resident_size 59

2017-03-09 21:29:00.488180 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:00.488253 LDAPM[491:46357] free 38.281250
2017-03-09 21:29:00.488273 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:00.488289 LDAPM[491:46357] used 1020.781250
2017-03-09 21:29:00.488308 LDAPM[491:46357] active 563.328125
2017-03-09 21:29:00.488326 LDAPM[491:46357] inactive 267.046875
2017-03-09 21:29:00.488343 LDAPM[491:46357] wired 190.406250
2017-03-09 21:29:00.488382 LDAPM[491:46357] purgableMemory 0.062500
2017-03-09 21:29:00.488416 LDAPM[491:46357] resident_size 126

2017-03-09 21:29:02.915796 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:02.915882 LDAPM[491:46357] free 46.578125
2017-03-09 21:29:02.915902 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:02.915917 LDAPM[491:46357] used 844.250000
2017-03-09 21:29:02.915935 LDAPM[491:46357] active 448.140625
2017-03-09 21:29:02.915952 LDAPM[491:46357] inactive 204.609375
2017-03-09 21:29:02.915970 LDAPM[491:46357] wired 191.500000
2017-03-09 21:29:02.915993 LDAPM[491:46357] resident_size 63

2017-03-09 21:29:05.359697 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:05.359786 LDAPM[491:46357] free 39.750000
2017-03-09 21:29:05.359810 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:05.359858 LDAPM[491:46357] used 730.562500
2017-03-09 21:29:05.359885 LDAPM[491:46357] active 363.437500
2017-03-09 21:29:05.359904 LDAPM[491:46357] inactive 176.109375
2017-03-09 21:29:05.359922 LDAPM[491:46357] wired 191.015625
2017-03-09 21:29:05.359979 LDAPM[491:46357] resident_size 53

2017-03-09 21:29:08.239699 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:08.239776 LDAPM[491:46357] free 36.656250
2017-03-09 21:29:08.239797 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:08.239813 LDAPM[491:46357] used 621.703125
2017-03-09 21:29:08.239831 LDAPM[491:46357] active 288.640625
2017-03-09 21:29:08.239848 LDAPM[491:46357] inactive 142.781250
2017-03-09 21:29:08.239865 LDAPM[491:46357] wired 190.281250
2017-03-09 21:29:08.240040 LDAPM[491:46357] resident_size 39

2017-03-09 21:29:11.167796 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:11.167871 LDAPM[491:46357] free 34.109375
2017-03-09 21:29:11.167893 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:11.168042 LDAPM[491:46357] used 638.437500
2017-03-09 21:29:11.168088 LDAPM[491:46357] active 311.562500
2017-03-09 21:29:11.168244 LDAPM[491:46357] inactive 149.296875
2017-03-09 21:29:11.168373 LDAPM[491:46357] wired 177.625000
2017-03-09 21:29:11.168492 LDAPM[491:46357] resident_size 85

2017-03-09 21:29:13.845295 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:13.845364 LDAPM[491:46357] free 41.937500
2017-03-09 21:29:13.845385 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:13.845401 LDAPM[491:46357] used 637.250000
2017-03-09 21:29:13.845419 LDAPM[491:46357] active 321.453125
2017-03-09 21:29:13.845440 LDAPM[491:46357] inactive 145.171875
2017-03-09 21:29:13.845458 LDAPM[491:46357] wired 170.640625
2017-03-09 21:29:13.845522 LDAPM[491:46357] resident_size 152

2017-03-09 21:29:13.855866 LDAPM[491:46357] applicationDidReceiveMemoryWarning
Message from debugger: Terminated due to memory issue

可以發現在我們的 app 內存使用正常的情況下,free 內存、 active 內存以及 app 當前的物理內存變化都是很正常的,以 100 為單位變化。但是后期 app 要求分配過多內存的時候,發現 active 內存不再以 100 為額度增長,這個時候應該是操作系統基于 jetsam 機制開始殺死一些后臺優先級低的 app 了。再進一步看,發現內存緊張的時候,purgeableMemory 不再打印了,是因為它的值變為了 0,所以我沒有繼續打印,也符合剛才的描述,在內存緊張時,可以自動釋放的內存,這一手段也可以用來降低內存峰值。

Q & A

1.為什么 app 崩潰的時候,我的應用使用內存不多,但是系統剩余內存很少的情況下,首先殺掉了我的應用呢?

這個問題,我以前也比較好奇,覺得這個不符合常理。但是通過剛才的測試來看,在自身物理內存不斷申請的情況下,當物理內存過大的時候,通過以上代碼收集到的值并不是很大,反而變小了。可能是代碼的問題,也可能是操作系統的問題。但是你所使用的代碼或者第三方平臺的收集策略很可能與此類似。收集到一個很小的值,但是并不代表你的 app 的內存使用情況很正確,很可能存在內存暴漲,但是收集結果卻沒有顯示出來。

2. 為什么上面的數據 free memory + used memory (active + inactive + purgeableMemory) 的值不等于 TotalMemory 呢?

這個問題我也很好奇,而且會發現當你自身 app 內存申請不合理的時候 free memory + used memory 的值也會和正常情況下不同,目前只能猜測系統用這塊內存來做了什么神秘的事情,如有對操作系統熟悉的同學,望告知!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,662評論 25 708
  • 概述 我們都知道一個進程是與其他進程共享CPU和內存資源的。正因如此,操作系統需要有一套完善的內存管理機制才能防止...
    SylvanasSun閱讀 3,879評論 0 25
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • 先來看下蘋果文檔: Memory Management Programming Guide for Core Fo...
    少少白閱讀 4,434評論 4 18
  • 今天上午去姐姐家和大姨,一起聊天。姐姐說了一句最讓我有感觸的話那就是:“先干著吧,如果我要是你的話,對自己現...
    兩條腿走路的喵閱讀 333評論 0 0