iOS內存深入探索之內存用量

前言

我們在查看iOS應用內存時,最常見的手法就是查看左邊的Debug Navigator。不知你是否也曾困惑於這個內存究竟包括哪些部分,或者使用Allocations模版觀察內存時發現無法和Debug Navigator顯示的內存匹配上,這篇文章將帶你解答這些疑惑。

iOS内存深入探索之内存用量

Debug Navigator VS Allocations

我們運行一個很簡單的iOS App,我只在ViewController中放置了一個View,然後對比下Debug Navigator 和 Allocations給出的內存用量。

iOS内存深入探索之内存用量
iOS内存深入探索之内存用量

可以發現,Debug Navigator給出的是79.3M,而Allocations統計的所有堆和相關VM加起來才38.72M,相差的還是很多的。在之前的文章中我有介紹關於Allocations和VM Tracker的深入理解,其實Allocations中主要包含的是所有MALLOC_XXX VM Region和部分App進程創建的VM Region。非動態的內存,以及部分其他動態庫創建的VM Region並不在Allocations的統計範圍內。比如主程序或者動態庫的_DATA數據段,這些數據內存區域並非通過malloc分配,也就沒有統計在All Heap Allocations中,所以你會發現All Heap Allocations往往會比較小。除非你自行使用malloc系列方法創建大內存塊,否則很難看到All Heap Allocations有一個大的數值。我們在實際的App中,大的內存佔用一般都是類似於WebKit,ImageIO,CoreAnimation等虛擬內存區域(VM Region),這些VM Region一般由系統代碼生成和管理,我們編寫的代碼如果間接引用了這些內存而沒有釋放,也就會造成大面積的內存洩漏。

Debug Navigator VS VM Tracker

接下來我們來看看VM Tracker統計的內存如何,下面是截圖。

iOS内存深入探索之内存用量

在看VM Tracker時,我們主要看Dirty Size和Swapped Size,由於我是在模擬器上調試的,所以才需要關注Swapped Size,在手機上,主進程的內存應該是不會交換到硬盤上的,內存不足時,會觸發內存警告。Dirty Size主要指的是不可被重新載入的內存區域大小,比如函數棧,如果你把函數棧的數據給抹掉了,也就無法恢復之前的函數調用棧數據了,這種可以稱為Dirty內存區域,但如果是通過文件內存映射載入到內存區域的,可以先清除掉這部分內存裡的數據暫時把這部分內存給別人用,需要時再從文件載入到內存,這種內存區域可以認為是非Dirty的。Dirty Size可以代表一個進程需求的最少內存量,當然在模擬器上,還要加上被交換出去的數據大小,即Swapped Size。

我們回到上圖,VM Tracker給出的Dirty Size總量時69.79M,還是和79.3M有些差距。不過我們可在在圖中看到_DATA數據段,Stack(函數棧)等等Allocations沒有統計的內存區域。

Debug Navigator VS vmmap command line

蘋果除了Instrument的VM Tracker可以查看虛擬內存之外,還有一個vmmap命令行可以查看進程的虛擬內存分配。使用模擬器啟動App,通過Activity Monitor找到App的進程ID,比如1364,使用vmmap查看它的虛擬內存分配。

vmmap 1364

結果如下

... VIRTUAL RESIDENT    DIRTY  SWAPPED VOLATILE   NONVOL    EMPTY   REGION REGION TYPE SIZE     SIZE     SIZE     SIZE     SIZE     SIZE     SIZE    COUNT (non-coalesced) =========== ======= ========    =====  ======= ========   ======    =====  ======= Activity Tracing 256K      36K      36K      12K       0K      36K       0K        2 CoreAnimation 36.1M    33.3M    33.3M    2848K       0K    33.3M       0K        2 Kernel Alloc Once 8K       8K       8K       0K       0K       0K       0K        2 MALLOC guard page 48K       0K       0K       0K       0K       0K       0K       13 MALLOC metadata 260K      92K      92K      32K       0K       0K       0K       16 MALLOC_LARGE 4360K    4256K    4256K     104K       0K       0K       0K        3         see MALLOC ZONE table belowMALLOC_LARGE (empty) 3988K    2080K    2080K    1908K       0K       0K       0K        2         see MALLOC ZONE table belowMALLOC_LARGE metadata 4K       4K       4K       0K       0K       0K       0K        2         see MALLOC ZONE table belowMALLOC_NANO 16.0M    2160K    2160K       0K       0K       0K       0K        3         see MALLOC ZONE table belowMALLOC_SMALL 40.0M     840K     840K     356K       0K       0K       0K        3         see MALLOC ZONE table belowMALLOC_TINY 8192K     320K     320K      36K       0K       0K       0K        3         see MALLOC ZONE table belowPerformance tool data 316K     264K     264K      52K       0K       0K       0K        3         not counted in TOTAL belowSTACK GUARD 56.0M       0K       0K       0K       0K       0K       0K        4 Stack 9232K      76K      76K      20K       0K       0K       0K        7 __DATA 35.4M    16.6M    16.4M    12.3M       0K       0K       0K      282 __FONT_DATA 4K       0K       0K       0K       0K       0K       0K        2 __LINKEDIT 96.0M    69.5M       0K       0K       0K       0K       0K      225 __TEXT 228.3M    54.4M       4K       4K       0K       0K       0K      225 __UNICODE 560K     320K       0K       0K       0K       0K       0K        2 mapped file 28.7M    2116K       0K       0K       0K       0K       0K        3 shared memory 44K      24K      24K       8K       0K       0K       0K        5 =========== ======= ========    =====  ======= ========   ======    =====  ======= TOTAL 562.9M   185.8M    59.3M    17.5M       0K    33.4M       0K      786

我們將Dirty Size和Swapped Size總量相加59.3M + 17.5M = 76.8M,和Debug Navigator給的值已經很相近了,我們再看上面的表格,發現有一行是這麼寫的Performance tool data ... not counted in TOTAL below,Performance tool data並沒有統計在最下面的TOTAL中,因為這些數據是Debug時提供調用回溯數據用的,所以vmmap默認認為沒有價值,沒有統計。但是Debug Navigator不這麼認為,我們加上Performance tool data的內存用量,264K + 52K = 316K = 0.308M,加上之前的76.8M就是77.108M,由於本次並沒有使用Instruments進行profile,所以佔用的內存會少一些,Debug Navigator顯示的剛好是77.1M。至於為什麼vmmap顯示的數據要比Instruments VM Tracker的要完整,目前我還沒有明確的答案。

shared memory

最後我要提到的時共享內存,共享內存可以提供跨進程訪問的能力,不過如果你的App使用了別的進程創建的共享內存,那麼Debug Navigator是不會將它計入你自己的內存總量的,不過vmmap會將它加入TOTAL中,所以可能會導致vmmap計算的內存量會大於Debug Navigator統計內存量。由於目前iOS對於shared memory的一些API並不支持,我也沒有深入研究,只是在OSX中驗證了這一點。

總結

最後來總結一下,Debug Navigator其實就是統計了當前進程的所有虛擬內存的Dirty Size + Swapped Size,當然還要剔除掉對第三方共享內存的使用量,當我們發現Debug Navigator的內存量飆高時,不僅僅要去關注Heap上的內存用量,更要關注VM Tracker中那些大Dirty Size的VM Region,這樣才能更透徹的瞭解你的App究竟是怎樣使用內存的。

鏈接:https://www.jianshu.com/p/827996b7aed0


分享到:


相關文章: