1. 程式人生 > >iOS開發記憶體優化之自動檢測記憶體洩露,檢查是否有迴圈引用,檢查記憶體為何如此大,Block迴圈引用的檢查

iOS開發記憶體優化之自動檢測記憶體洩露,檢查是否有迴圈引用,檢查記憶體為何如此大,Block迴圈引用的檢查

手機裝置的記憶體是一個共享資源。應用程式可能會不當的耗盡記憶體、崩潰,或者遭遇大幅度的效能降低。

Facebook iOS客戶端有很多功能,並且它們共享同一塊記憶體空間。如果任何特定的功能消耗過多的記憶體,就會影響到整個應用程式。這是可能發生的,比如,這個功能導致了記憶體洩露。

當我們分配了一塊記憶體,並設定了物件之後,如果在使用完了之後忘記釋放,這就會發生記憶體洩露。這意味著系統是無法回收記憶體並交予他人使用,這也最終意味著我們的記憶體將會逐漸耗盡。

在Facebook,我們有很多工程師在程式碼庫的不同部分上工作。這不可避免的會發生記憶體洩露。當發生記憶體洩露之後,我們需要儘快找到並修復它們。

一些工具已經可以找到記憶體洩露,但是它們需要大量的人工干預:

  1. 開啟Xcode,給效能分析(profiling)編譯。
  2. 載入Instruments。
  3. 使用應用程式,嘗試儘可能多的重現場景和行為。
  4. 檢視記憶體和洩露。
  5. 追蹤記憶體洩露的根源。
  6. 修復這個問題。

這意味著每次都需要重複大量的手動操作。為此,在我們的開發週期上,我們可能無法儘可能早的定位和修復記憶體洩露問題。

自動化可以在不需要更多開發者的情況下,更快的找到記憶體洩露。為了解決這個問題,我們做了一套工具來自動化的處理和修復我們程式碼庫中的一些問題。今天,我們很興奮的釋出這些工具: FBRetainCycleDetector 、 FBAllocationTracker

 、 FBMemoryProfiler 。 

迴圈引用(Retain cycles)

Objective-C 使用引用計數去管理記憶體和釋放不使用的物件。記憶體中的任何一個物件都可以持有( retain )其他的物件,只要前面的物件需要它,物件就會一直保持在記憶體中。檢視這個的一個方法是這個物件持有其他的物件。 

在大部分時間內,這都工作的很好,但當兩個物件互相持有的時候,這就會陷入一個僵局。直接,或者更常見的,通過間接物件連線它們。這種持有引用的環我們叫做迴圈引用(Retain cycles)。

迴圈引用會導致一些列的問題。最好的情況下,物件只會在記憶體中佔有一點點位置。如果這個被洩露的物件正積極地做個一些不平凡的事情,應用程式的其他部分就只會有更少的記憶體。最壞的情況下,如果洩露導致使用超出可用記憶體的容量,那麼,應用程式會崩潰。

在手動效能分析期間,我們發現,我們往往有一些迴圈引用。我們很容易引起記憶體洩露,但是很難找到它們。迴圈引用檢測器可以很容易的找到它們。

在執行時檢測迴圈引用

在 Objective-C 中找迴圈引用類似於在一個有向無環圖(directed acyclic graph)中找環, 而節點就是物件,邊就是物件之間的引用(如果物件A持有物件B,那麼,A到B之間就存在著引用)。我們的 Objective-C 物件已經在我們的圖中,我們要做的就是用深度優先搜尋遍歷它。

這有點抽象,但效果很好。我們必須確保我們可以像節點一樣使用物件,對於每個物件,我們都可以獲取到它引用的所有物件。這些引用可能是 weak ,也可能是 strong 。只有強引用才會導致迴圈引用。對於每個物件來說,我們需要知道如何找出這些引用。 

幸運的是,Objective-C提供了一個強有力的、內省的執行時庫。這讓我們在圖中可以有足夠的資料去挖掘。

圖中的節點可以是物件,也可以是Block。讓我們來分別討論一下。

物件

執行時有很多工具允許我們對物件進行內省。

我們要做的第一件事是獲取物件的例項變數的佈局(ivar layout)。

const char *class_getIvarLayout(Class cls);
const char *class_getWeakIvarLayout(Class cls);

對於物件,例項變數的佈局描述了我們在哪兒可以找到其他物件的引用。它會提供給我們一個索引(index),這代表我們需要在物件地址上新增一個偏移量(offset),就可以得到它所引用的物件的地址。執行時也允許我們獲取“弱引用例項變數佈局(weak ivar layout)”。

這也部分支援Objective-C++。在Objective-C++中,我們可以在結構體中定義物件,但是這不會在例項變數佈局中獲取到。執行時提供了“型別編碼(type encoding)”來處理這個問題。對於每一個例項變數來說,型別編碼描述了變數是如何結構化的。如果這是一個結構體,它會描述它包含了哪些欄位和型別。我們計算出它們的偏移量,在圖中,找出它們所指向的物件。

也有一些邊緣條件我們不會深入。大部分是一些不同的集合,我們不得不列舉它們去獲得它們持有的物件,這可能會導致一些副作用。

Block

Block和物件有一點不一樣。執行時不會讓我們很輕易的看到它們的佈局,但是我們仍然可以猜測。

在處理Block的時候,我們可以使用 Mike Ash 在他的專案 Circle (第一時間啟發 FBRetainCycleDetector 的專案)中提出的想法。 

我們可以使用的是 ABI (application binary interface for blocks - 應用程式二進位制Block介面)。它描述了Block在記憶體中的樣子。如果我們知道我們在處理的引用是一個Block,我們可以把它丟在一個假的結構體中來模仿Block。在放到一個C語言的結構體之後,我們可以知道Block所持有的物件。不幸的是,我們不知道這些引用是強引用還是弱引用。 

為了解決這個問題,我們使用了一個黑盒技術。我們建立一個物件來假扮我們想要調查的Block。因為我們知道Block的介面,我們知道在哪可以找到Block持有的引用。我們偽造的物件將會擁有“釋放檢測(release detectors)”來代替這些引用。釋放檢測器是一些很小的物件,它們會觀察傳送給它們的釋放訊息。當持有者想要放棄它的持有的時候,這些訊息會發送給強引用物件。當我們釋放我們偽造的物件的時候,我們可以檢測哪些檢測器接收到了這些訊息。只要知道哪些索引在偽造的物件的檢測器中,我們就可以找到原來Block中實際持有的物件。

自動化

讓這工具真正閃光的是,在工程師內部構建的時候,它會連續的、自動的執行。

客戶端部分自動化是簡單的。我們在定時器上執行迴圈引用檢測器,定期掃描記憶體去尋找迴圈引用,雖然這不是完全沒有問題。當我們第一次執行分析器的時候,我們意識到它不足以很快的掃描整個記憶體空間。當它開始檢測的時候,我們需要給它提供一組候選物件。

為了更有效的解決這個問題,我們開發了 FBAllocationTracker 。這個工具會主動跟蹤 NSObject 子類的建立和釋放。它可以以一個很小的效能開銷來獲取任何類的任何例項。 

現在,讓我們來仔細看看後臺會發生什麼。

迴圈引用可以包含任何數量的物件。一個壞的連線會導致很多環的時候,這就複雜了。

在環中,A→B是一個壞連線,建立了兩個環:A-B-C-D 和 A-B-C-E。

這有兩個問題:

  1. 我們不想給一個壞連線導致的兩個迴圈引用分別標記。
  2. 我們不想給可能代表兩個問題的兩個迴圈引用一起標記,即使它們共享一個連線。

所以我們需要給迴圈引用定義簇組(clusters),鑑於這些啟發,我們寫了個演算法來找到這些問題。

  1. 在給定的時間收集所有的環。
  2. 對於每一個環,提取Facebook特定的類名。
  3. 對於每一個環,找到包含在環內的被報告的最小的環。
  4. 依據上面的最小環,將環新增到組中。
  5. 只報告最小環。

最後一部分是找出誰第一時間偶然引入了迴圈引用。我們可以通過環中的”git/hg責任”的部分程式碼來猜測最近的變化所導致的問題。最後一個接觸這個程式碼的人將會收到修復程式碼的任務。

整個系統如下:

手動效能分析

雖然自動化有助於簡化發現迴圈引用的過程,降低人員的消耗,手動效能分析依然有它的用武之地。我們建立的另一個工具允許任何人檢視記憶體使用,甚至不需要把他的手機插到電腦上。

生成(Generations)

FBMemoryProfiler 的一個很偉大的特性是“生成追蹤(generation tracking)”,類似於蘋果的Instruments的生成追蹤。生成只是簡單的在兩次標記之間拍攝所有仍然活著的物件的快照。 

使用 FBMemoryProfiler 的介面,我們可以標記生成,例如,分配三個物件。然後我們標記另一個生成,之後繼續分配物件。第一個生成包含我們一開始的三個物件。如果任意一個物件被釋放了,它會從我們第二個生成中移除。 

當我們有一個重複的任務,我們認為可能會記憶體洩露的時候,生成追蹤是很有用的,例如,導航View Controller的進出。在每次開始我們的任務的時候,我們標記一個生成,然後,對之後的每個生成進行調查。如果一個物件不應該活這麼長時間,我們可以在 FBMemoryProfiler 介面清楚地看到。 

Check Out

無論你的應用程式是大是小,功能是多是少,好的工程師都應有好的記憶體管理。在這些工具的幫助之下,我們可以更簡單的找到並修復這些記憶體洩露,所以我們可以花費更少的時間去手動處理,這樣就可以有更多的時間去編寫更好的程式碼。我們也希望你可以發現它們是有用的。在Github上check out下來吧。 FBRetainCycleDetector , FBAllocationTracker 和 FBMemoryProfiler 。

相關推薦

iOS開發記憶體優化自動檢測記憶體洩露檢查是否迴圈引用檢查記憶體為何如此Block迴圈引用檢查

手機裝置的記憶體是一個共享資源。應用程式可能會不當的耗盡記憶體、崩潰,或者遭遇大幅度的效能降低。 Facebook iOS客戶端有很多功能,並且它們共享同一塊記憶體空間。如果任何特定的功能消耗過多的記憶體,就會影響到整個應用程式。這是可能發生的,比如,這個功能導致了記

iOS App記憶體優化 解決UIImagePickerController的圖片物件佔用RAM過高問題

這個坑會在特定的情況下特別明顯: 類似朋友圈的新增多張本地選擇\拍照 的圖片 並在介面上做一個預覽功能 由於沒有特別的相機\相簿需求,則直接使用系統自帶的UIImagePickerController 最簡單的方法-> UIImagePickerController 選擇圖片 -> 代理返回圖

android記憶體優化記憶體分析工具的使用

 anroid記憶體分析工具的使用 一.Eclipse Heap分析記憶體洩露           Android開發中避免不了碰到記憶體洩露問題,這裡先大概講下記憶體洩露的基本概念:記憶體洩露官方的解釋是是用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,結果導致一直

android記憶體優化webview

在混合型app中它是主角,一切由它呈現,如58同城,趕集網等;在另一些超級app中亦有它的影子,微信,qq,支付寶,沒有一個超級app能少了它,既能展示最新最潮的實時資訊,又能扮演盤踞一方的全功能型網站,與native結合後又能扮演諸如公眾號之內的應用等等,其能力可想而知。webview在android端的演

cocos2dx 圖片記憶體優化從200M到50M

公司專案曾經出過一個非常牛逼的問題,由於我感覺一個手機數百M記憶體,怎麼還能不夠用??以至於後來導致遊戲卡到爆,卡到我內心萬馬奔騰的時候我決定,我要優化記憶體。 不過程式碼都寫了如果要動程式碼那我真心日

Android-記憶體優化OOM

Android的記憶體優化是效能優化中很重要的一部分,而避免OOM又是記憶體優化中比較核心的一點,這是一篇關於記憶體優化中如何避免OOM的總結性概要文章,內容大多都是和OOM有關的實踐總結概要。理解錯誤或是偏差的地方,還請多包涵指正,謝謝! (一)Android的記憶體

OC--記憶體管理自動釋放[NSMutableArray array]生成的自動釋放陣列被自動釋放後引起的嚴重後果

在做一個TableView程式時,要在表格裡顯示一個資料夾內所有檔案的清單,程式在一開始顯示時正常,但是一滾動視窗時就崩潰,查詢這個錯誤整整花了我一天的時間,原來出在NSMutableArray初始化時用的方法不正確,都是因為Objective-C的基礎知識沒學好。 在

Android記憶體優化——記憶體洩漏篇

原文地址:http://blog.csdn.net/ys408973279/article/details/50389200 在Android開發中,我們經常會使用到static來修飾我們的成員變數,其本意是為了讓多個物件共用一份空間,節省記憶體,或者是使用單例模式,

iOS性能優化Leaks動態分析

反向輸出 ges 合並 性能優化 recursion 問題 details auto 14. iOS性能優化之Leaks動態分析 Instruments-Leaks有很多跟蹤模塊可以動態分析和跟蹤內存, CPU 和文件系統(因為是動態分析 所以必須運行才能打開)。 具體

iOS開發HTTPS實現信任SSL證書和自簽名證書

                                          &nb

Android應用優化程式碼檢測優化

前言 最近換了新的公司,面對新的程式碼大家都有不同的熟悉過程和方法。在我的角度來說,利用程式碼檢測工具,可以更直接地去熟悉程式碼邏輯和業務邏輯,表現得自己去程式碼質量很有追求,最重要當然是在公司的任務管理工時上面顯得自己積極向上啦。不過在修改程式碼之前,你要根據專案的分工、明確在公司

iOS開發實用軟體NWPusher

一個App獲取到一個Push內容。必須是伺服器拿著對應token和需要的訊息內容發給蘋果伺服器,然後由蘋果伺服器推送給使用者。作為前端開發人員,為了學習Push,去搭建一個伺服器,還是很耗時的。想了解iOS推送系列之Push的工作原理,可以檢視這篇部落格。 現在

Angular效能優化檢測

Angular效能優化之髒檢測 當我們在使用 Angular 框架搭建專案時,隨著元件越來越多,頁面也來越複雜,效能會越來越低,主要表現在 CPU 使用率 很高。所以我們要對專案做一定的優化。 Angular髒檢查(Change Detection)機制 Angular

IOS開發教程系列第2集 開發工具Xcode

2018年11月07日 17:19:24 碼子哥 閱讀數:4 標籤: ios ios開發 xc

iOS開發bug消滅:Invalid update: invalid number of rows in section 0.

Invalid update: invalid number of rows in section 0. swift版本:3.0 Xcode版本:8.0 錯誤全文: Invalid update: invalid number of rows in

iOS開發 URL編碼對特殊符號的處理

       在iOS開發的過程中,我們在網路請求以及載入webView的時候都會用到URL, 眾所周知如果URL會含有中文會識別不了,這樣我們就要對URL進行編碼!      蘋果給我們提供的方法,

IOS開發基礎】判斷NSString為純數字

//判斷是否為整形: - (BOOL)isPureInt:(NSString*)string{     NSScanner* scan = [NSScanner scannerWithString:string];     int val;     return

iOS 網路請求優化取消請求

頁面返回的時候,將網路請求取消 同一個請求多次請求時,短時間忽略相同的請求 同一個請求多次請求時,取消之前發出的請求 傳送的請求,多次嘗試並確保成功 最近發現很多網路請求都有可以優化的地方,雖然開發和測試都沒有發現問題,但是可以讓程式碼更加的優雅。想到

[iOS開發必備工具]簡單的全景展示利器panoramagl

    現在越來越多的樓盤,房屋租賃公司都玩起時尚 ,用手機,平板的全景圖片展示來房屋的資訊,利用360度,720度的全景展示,可以很容易給人一種產體的感覺,對於房地產行業還是蠻有意思的,現在真是有什麼需求,就有什麼答案呀,這不,codeGoogle都已經為我們做了一個簡單的

iOS開發網路資料AFNetworking使用

如何選擇AFNetworking版本 官網下載2.5版本:http://afnetworking.com/ 此文章基於AFNetworking2.0,如果您使用的是2.5版本的,請看這篇文章:AFNetworking2.5使用 首先得下載AFNetworking庫檔案,下載