WWDC 2018 iOS 記憶體深入研究

2021-10-02 12:36:52 字數 4515 閱讀 9710

按照 session 的套路,我們先看一下提綱:

那我們就按順序開始啦!

記憶體是由系統管理,一般以頁為單位來劃分。在 ios 上,每一頁包含 16kb 的空間。一段資料可能會占用多頁記憶體,所占用頁總數乘以每頁空間得到的就是這段資料使用的總記憶體。

記憶體頁按照各自的分配和使用狀態,可以被分為cleandirty兩類。

以上面的**為例,申請一塊長度為 80000 位元組的記憶體空間,按照一頁 16kb 來計算,就需要 6 頁記憶體來儲存。

如下圖所示,乙個 50kb 的被載入到記憶體中時,需要分配 4 頁記憶體來儲存。其中第四頁中有 2kb 的空間會被用來儲存這個的資料,剩餘空間可能會被用來儲存其它資料。

當記憶體不足的時候,系統會按照一定策略來騰出更多空間供使用,比較常見的做法是將一部分低優先順序的資料挪到磁碟上,這個操作稱為page out。之後當再次訪問到這塊資料的時候,系統會負責將它重新搬回記憶體空間中,這個操作稱為page in

clean memory

dirty memory

值得注意的是,在使用 framework 的過程中會產生dirty memory,使用單例或者全域性初始化方法是減少dirty memory不錯的方法,因為單例一旦建立就不會銷毀,全域性初始化方法會在 class 載入時執行。

compressed memory

當記憶體吃緊的時候,系統會將不使用的記憶體進行壓縮,直到下一次訪問的時候進行解壓。

例如,當我們使用dictionary去快取資料的時候,假設現在已經使用了 3 頁記憶體,當不訪問的時候可能會被壓縮為 1 頁,再次使用到時候又會解壓成 3 頁。

假設**中的cache已被壓縮過

事實上,當你嘗試去再次訪問 cache 物件的時候,系統會先解壓這塊記憶體

這個過程中記憶體使用會增加,在記憶體吃緊的時候,這並不是我們想要的。隨後,當我們會執行大量工作去清空 cache,最終得到的記憶體空間和記憶體壓縮的結果一樣

所以,相比以往的快取手段,更加建議去調整策略,例如減少快取使用,或者在收到記憶體警告的時候,將這類事情交由系統去處理。

我們對資料進行快取的目的是想減少 cpu 的壓力,但是過多的快取又會占用過大的記憶體。由於記憶體壓縮機制的存在,我們需要根據快取資料大小以及重算這些資料的成本,在 cpu 和記憶體之間進行權衡。

在一些需要快取資料的場景下,可以考慮使用nscache代替nsdictionary,因為nscache可以自動清理記憶體,在記憶體吃緊的時候會更加合理。

通常情況下,我們所說的記憶體占用是指dirty memorycompressed memoryclean memory不需要過多關心。

當你使用 xcode 10 以前的版本進行除錯時,在記憶體過大時,debug session 會直接終止,並且在控制台列印出異常。從 xcode 10 開始,debugger 會自動捕獲exc_resource resource_type_memory異常,並斷點在觸發異常丟擲的地方,十分方便定位問題。

通過這個工具,可以很直觀地檢視記憶體中所有物件的記憶體使用情況,以及相互之間的依賴關係,對定位那些因為迴圈引用導致的記憶體洩露問題十分有幫助。

簡單介紹一下相關的命令

vmmap - 檢視虛擬記憶體

檢視詳細報告

vmmap xx.memgraph

檢視摘要報告

vmmap --summary xx.memgraph

配合管道命令檢視所有動態庫的ditry pages的總和

vmmap -pages ***.memgraph | grep '.dylib' | awk ' end '

只顯示cg image相關的資料

vmmap xx.memgraph | grep 'cg image'

更多使用方式請檢視vmmap的文件

man vmmap

leaks - 檢視****存

檢視是否有記憶體洩露

leaks xx.memgraph

檢視某處記憶體的洩漏

leaks --tracetree [記憶體位址] xx.memgraph

更多使用方式請檢視 leaks 的文件

man leaks

heap - 檢視堆區記憶體

檢視所有堆區物件的記憶體使用

heap xx.memgraph

預設情況下是按照物件數量進行排序,通常情況下它們不會造成什麼記憶體問題。我們需要關心的是那些為數不多,卻占用了大量記憶體的物件,這時候就可以增加引數-sortbysize,按照記憶體占用大小順序來檢視所有堆區物件的記憶體使用

heap xx.memgraph -sortbysize

當確定是哪個型別的物件占用了太多記憶體之後,可以得到每個物件的記憶體位址

heap xx.memgraph -addresses all | 'xxbigdata'

更多使用方式請檢視 heap 的文件

man heap

enabling malloc stack logging

product -> scheme -> edit scheme -> diagnostics中,開啟malloc stack功能,建議使用live allocations only選項

之後 lldb 會記錄除錯過程中物件建立的堆疊,配合malloc_history工具,就可以定位到那些占用了過大記憶體的物件是**建立的。

malloc_history - 檢視記憶體分配歷史

malloc_history xx.memgraph [address]

malloc_history xx.memgraph --fullstacks [address]

更多使用方式請檢視 malloc_history 的文件

man malloc_history

上面講述了那麼多的分析工具,那我們應該選擇哪種工具呢?蘋果的工程師幫我們做了如下整理:

大家可以根據上圖所示,根據不同的需要進行選擇。

對於 ios 系統而言,絕大部分場景下哪類資料佔記憶體最多呢?當然是!需要注意的是,所佔記憶體的大小與的尺寸有關,而不是的檔案大小。

例如:有乙個 590kb 的,解析度是 2048px * 1536px,它實際使用的記憶體不是 590kb,而是2048 * 1536 * 4 = 12 mb。。

還有佔記憶體更小的格式:

選擇正確的格式可以減少了記憶體的使用。簡單總結一下:

乙個位元組:alpha 8

兩個位元組:亮度和alpha 8

四個位元組:srgb

八個位元組:wide 格式

複製**

那下乙個話題來了,如何選擇正確的格式呢?

簡單的回答是:不需要你來選擇格式,而是應該讓格式選擇你。是不是覺得一下子松了一口氣?哈哈?

用 uigraphicsimagerenderer 代替 uigraphicsbeginimagecontextwithoptions

使用uigraphicsbeginimagecontextwithoptions生成的,每個畫素需要 4 個位元組表示。建議使用uigraphicsimagerenderer,這個方法是從 ios 10 引入,在 ios 12 上會自動選擇最佳的影象格式,可以減少很多記憶體。

另外,如果想修改顏色,可以直接修改 tintcolor,不會有額外的記憶體開銷。

downsampling

當你縮小一幅影象的時候,會按照取平均值的辦法把多個畫素點變成乙個畫素點,這個過程稱為downsampling

uiimage 在設定和調整大小的時候,需要將原始影象加壓到記憶體中,然後對內部座標空間做一系列轉換,整個過程會消耗很多資源。我們可以使用 imageio,它可以直接讀取影象大小和元資料資訊,不會帶來額外的記憶體開銷。

記憶體是乙個有限的共享資源,要學會使用 xcode 分析記憶體工具,從而了解應用程式記憶體占用情況,並使用一些縮減應用程式記憶體占用空間的技巧和竅門。

ps:有理解認知不正確的,歡迎指正!

蘋果WWDC14 iOS8新特性

遊戲應用 遊戲優化,大型遊戲 3d場景so easy!metal 能夠減少opengl的負載,讓開發者更好的錄用裝置的晶元效能,從而大幅度提公升圖形和遊戲能力,讓之前無法在平板電腦上實現的情景成為了可能。spritekit 3d場景渲染器,專門針對休閒遊戲的以及3d遊戲的乙個優化,並且擁有更強的物理...

WWDC2014之iOS使用動態庫1

wwdc2014上發布的xcode6 beta版有了不少更新,其中令我驚訝的乙個是蘋果在ios上開放了動態庫,在xcode6 beta版的更新文件中是這樣描述的 在其它大部分平台上,動態庫都可以用於不同應用間共享,這就大大節省了記憶體。從目前來看,ios仍然不允許程序間共享動態庫,即ios上的動態庫...

ios 內購資料

如果您正在尋找乙份手把手教你實現iap的教程的話,這篇文章不是您的菜。關於iap的實現和步驟,可以參考下面的教程 和相應的 ray wenderlich的 一篇 並茂的 中文教程 直接使用大神們封好的store有關的庫,比如 mattt cargobay robotmedia rmstore 或者m...