Instruments 效能檢測

發表於2016-12-30

先來一發蘋果官網上Instruments User Guide,其實沒啥用,英語不好的也懶得去看。(反正我是看不懂)

111440398-b523ad66638b643f

關於Instruments有網友如是說的:“一句話: 記憶體開銷、執行速度、記憶體洩露 and so on”。

如此簡單的回答肯定打發不了我們們各位看官和麵試官,當然上述表達和下邊的網友總結的意思是一樣的:

問:您一般是怎麼使用Instruments的?

這個問題也就是考察下你經驗如何了, Instruments裡面工具很多,也沒必要逐一說明,挑幾個常用的說下就好:

Time Profiler:分析程式碼的執行時間,找出導致程式變慢的原因。

Zombies:檢查是否訪問了殭屍物件,但是這個工具只能從上往下檢查,不智慧

Allocations:用來檢查記憶體分配,寫演算法的那批人也用這個來檢查

Leaks:檢查記憶體,看是否有記憶體洩露

還有對Instruments這麼理解的,說的也不錯:Instruments的價值在於,它使我們深刻理解我們程式碼的內部運作

好了,那麼就開始我們自己的Instruments之旅吧,揭開神祕面紗。

注:本文大部分篇幅將講述Allocations、Leaks、Time Profiler、Zombies這四項,因為是經常用到的,其他的可能簡單介紹或者一帶而過。

關於Instruments的概述請參考Instruments概述,可以整體的理解一下Instruments。

首先我們要知道怎麼開啟這個Instruments:在xcode中有好幾種開啟的方式,具體如下:

1、

121440398-0207284b7ddeb131

2、

131440398-d49417c06b4ec94e

3、長按啟動按鈕,選擇Profile

141440398-e8374b8185243d3e

Instruments頁面如下,裡面全是英文,筆者為大家用有道翻譯了一下,哪裡不對,希望大家告知,我進行修改。

151440398-e98e11e264232abe

其實我們可以看到xcode開發人員的用心,一些圖片上邊的標誌我們一看就能明白是什麼意思,例如能量診斷,其實就是手機上邊的電池嘛?,還有洩漏,就是一個管子,有水滴?滴下來了,就是洩漏的意思。還有網路,就是一個訊號塔發射訊號的意思。有的看不懂沒關係,我這不是給大家翻譯了嘛。

另外有的時候Instruments的啟動按鈕是啟動不了的,這怎麼辦呢,有大神給提供了一種解決方案:Instruments啟動按鈕不可點。(在這裡謝謝標哥大神,雖然你也不看我的部落格哈哈)

大概的解決方案就是:各種重啟。手機關機、重啟,同時將Xcode關掉,然後重新開啟Xcode,然後重新將手機與電腦連線,再開啟Instrument,點選Core Animation,切換到當前的手機,發現可以正常點選了。

在這裡有必要說一句,這19個效能檢測的工具,有的可以在模擬器上邊進行檢測,有的是不可以的。如果選擇使用ios模擬器,那麼它監控的就會是你的mac,和真機還是有區別的,譬如這個電池就應該用真機檢測,模擬器的電池沒啥可測的。而我們的目的是手機app,當然,所有的專案都是都可以在真機上檢測的。建議大家儘量在真機上用Istrunments。下邊我們就逐一看看這19個效能檢測是怎麼操作的,到底怎麼就檢測了呢,檢測出來的都是什麼東西呢,會得到什麼樣的結果呢,我們拭目以待。

1、Blank

空白的,沒啥可說的。

2、Activity Monitor 活動監視器

監測CPU、記憶體、硬碟和網路使用情況,還能檢測所有的程式,檢測父/子程式的層次。預設情況下顯示:虛擬記憶體大小,cup總使用量,cup使用者所使用的佔用比,cpu系統使用佔用比。推薦學習小白學習Activity Monitor

161440398-45d96b32f3ab6177

第1部分是profile的表頭,啟動、暫停、專案名稱、執行時間等等資訊都可以在上邊找到。

第2部分是左邊是效能檢測的專案名稱,右邊以豎形條形式展示執行過程中資料值的大小,比較直觀。

第3部分是具體的效能數值,可以選擇Details(詳細資訊)和Console(控制檯),可以看到具體的詳細資訊。

3、Allocations 分配:

(!!重點之一來了!!來得如此之快,還叫人有些不適應呢)

為什麼我們要使用這個Allocations,參考Alloactions簡單使用。文章中介紹了原因:

171440398-4e1370d4ae818970

Allocations 的頁面如下所示:

181440398-d68d941aa032c25b

1:堆區記憶體和虛擬記憶體佔用圖

2:堆區記憶體佔用圖

3:虛擬記憶體跟蹤圖

4:選擇使用不同的形式展示記憶體佔用情況

5:勾選讓上面曲線圖展示對應記憶體佔用情況

6:持久分配的記憶體所佔位元組數(未釋放)即該類物件在記憶體中佔得總記憶體

7:持久建立的物件個數(未釋放)即該物件存在於記憶體中的個數

8:臨時分配的物件個數(未釋放)即存在過已經被回收的物件的個數

9:分配的所有記憶體所佔位元組數(未釋放)

10:建立的物件總數(未釋放)

11:設定皮膚,不同的設定使左邊有不同展示效果

如上圖並不能很好的瞭解每個方法所佔用的記憶體情況,接下來我們點選4的call Trees如下圖設定:

191440398-c42e4fb92085a826

接下來我們根據記憶體洩漏的情況對記憶體分配進行分析,記憶體洩漏分兩種:

第一種:為物件A申請了記憶體空間,之後再也沒用到A,也沒有釋放A導致記憶體洩漏。這種是Leaked Memory記憶體洩漏。

第二種:類似於遞迴,不斷的申請記憶體導致的記憶體洩漏。根據下邊的連結文章我們知道這種情況就是Abandoned Memory被遺棄的記憶體。

說到這裡大家可以去看看這篇文章iOS Instruments,名字雖然起的很一般,但是講了Allocations的前因後果。當然,下文我也拿來借鑑了。在此,謝謝文章作者luobs。

第二種情況根據以下圖的操作可以清晰的找到對應的問題程式碼,當然不一定是我們自己程式碼的原因,也有可能是系統框架的問題。

201440398-d7b18f84b7056f06

211440398-5e47bfea247d855a

下邊是關於尋找這個Abandoned Memory被遺棄的記憶體的方法:

221440398-520b2f9411c5d60f

231440398-654d8bb7d708a585

該方法筆者沒有親測,但是看著挺可靠的,具體的步驟告訴了,按照步驟一步一步的走下來應該就能找到被遺棄的記憶體(看到這個“被遺棄”的詞,就想起了《紀念碑谷》)

4、Automation:自動化  

UI 自動測試是iOS 中重要的附加功能,它由名為“Automation”的新的工具物件支援。Automation工具的指令碼是用JavaScript語言編寫,主要用於分析應用的效能和使用者行為,模仿/擊發被請求的事件,利用它可以完成對被測應用的簡單的UI測試及相關功能測試。

簡單的說就是自己寫JS指令碼進行測試。(最好了解JS語言最好了,不瞭解的這個Automation其實有點雞肋)

可以參考下邊這篇文章借鑑他人UI Automation

241440398-d35280037b23748a

251440398-43529466b72887fa

261440398-29c30c91dbc5641e

5、Cocoa Layout:自動佈局 

Cocoa Layout可以應用於iOS模擬器和Cocoa桌面應用,但是不能和連線的iOS裝置一起使用。Cocoa Layout提供了一個與NSLayoutConstraint類的例項有關的所有事件的時間軸,這一點和回溯(backtrace)很像。

關於這項內容網上資料並不多,大家可以參考利用Cocoa Layout 檢視自動佈局

271440398-3c3a4ab5b8e35115

6、Core Animation:核心動畫 

 在網上查資料,大部分都是關於動畫怎麼設定的,和layer有關的動畫製作,即便前面加上Instruments,查出來的也是關於動畫製作的。。。也是醉了。幸好我們同事之前總結過關於這方面的,直接拿來用吧。如果大家有相關的連結,希望能給提供一下,謝謝了。

這裡我們需要知道這個Core Animation是幹什麼的,Core Animation工具是用來檢測Core Animation效能的。(看起來這句話好像說了等於沒說,不過從這句話裡面我們可以看出加粗的Core Animation是我們網上查到的利用iOS做的動畫,而前面沒有加粗的是我們們Istruments裡面的Core Animation工具)

首先我們瞭解什麼是FPS。FPS:一秒鐘渲染多少幀 Frame Per Second = FPS。

Core Animation給我們提供了週期性的FPS,並且考慮到了發生在程式之外的動畫,介面滑動FPS可以進行測試。一般FPS是60左右,過於低的話需要進行優化。根據下圖我們會發現,過於低的數值是低於45

 281440398-9f49d5bbafc95a97

291440398-d98cf216f7e82ea1

301440398-644c60029d66adb3

看上圖,我們主要介紹右邊“設定”裡面的相關內容。

Color Blended Layers混合過度繪製

這個選項基於渲染程度對螢幕中的混合區域進行綠到紅的高亮(也就是多個半透明圖層的疊加)。由於重繪的原因,混合對GPU效能會有影響,同時也是滑動或者動畫幀率下降的罪魁禍首之一。

這樣就能在模擬器上邊看到這個Color Blended Layers

311440398-3aa4c667ab8d6614

GPU每一幀可以繪製的畫素有一個最大限制(就是所謂的fill rate),這個情況下可以輕易地繪製整個螢幕的所有畫素。但是如果由於重疊圖層的關係需要不停地重繪同一區域的話,掉幀就可能發生了。

GPU會放棄繪製那些完全被其他圖層遮擋的畫素,但是要計算出一個圖層是否被遮擋也是相當複雜並且會消耗處理器資源。同樣,合併不同圖層的透明重疊畫素(即混合)消耗的資源也是相當客觀的。所以為了加速處理程式,不到必須時刻不要使用透明圖層。任何情況下,你應該這樣做:

給檢視的backgroundColor屬性設定一個固定的,不透明的顏色

設定opaque屬性為YES

注意下邊的內容為我們解釋了為什麼在tableview效能優化中我們提到的平衡CPU和GPU裡面關於Core Animation的原因。

(關於CPU和GPU可以參考CPU與GPU

如果用到了影像,儘量避免透明除非非常必要。如果影像要顯示在一個固定的背景顏色或是固定的背景圖之前,你沒必要相對前景移動,你只需要預填充背景圖片就可以避免執行時混色了。

如果是文字的話,一個白色背景的UILabel(或者其他顏色)會比透明背景要更高效。

Color Offscreen-Rendered Yellow(離屏渲染)

 

這裡會把那些需要離屏渲染的圖層高亮成黃色。這些圖層很可能需要用shadowPath或者shouldRasterize來優化。

當圖層屬性的混合體被指定為在未預合成之前不能直接在螢幕中繪製時,螢幕外渲染就被喚起了。螢幕外渲染並不意味著軟體繪製,但是它意味著圖層必須在被顯示之前在一個螢幕外上下文中被渲染(不論CPU還是GPU)。圖層的以下屬性將會觸發螢幕外繪製:

1、圓角(當和maskToBounds一起使用時)

2、圖層蒙板

3、陰影

螢幕外渲染和我們啟用光柵化時相似,除了它並沒有像光柵化圖層那麼消耗大,子圖層並沒有被影響到,而且結果也沒有被快取,所以不會有長期的記憶體佔用。但是,如果太多圖層在螢幕外渲染依然會影響到效能。

有時候我們可以把那些需要螢幕外繪製的圖層開啟光柵化以作為一個優化方式,前提是這些圖層並不會被頻繁地重繪。

對於那些需要動畫而且要在螢幕外渲染的圖層來說,你可以用CAShapeLayer,contentsCenter或者shadowPath來獲得同樣的表現而且較少地影響到效能。

Color Hits Greenand Misses Red(光柵化快取圖層命中情況)

當使用shouldRasterizep屬性的時候,耗時的圖層繪製會被快取,然後當做一個簡單的扁平圖片呈現。當快取再生的時候這個選項就用紅色對柵格化圖層進行了高亮。如果快取頻繁再生的話,就意味著柵格化可能會有負面的效能影響了。

Color Copied Images 拷貝的圖片

有時候寄宿圖片的生成意味著Core Animation被強制生成一些圖片,然後傳送到渲染伺服器,而不是簡單的指向原始指標。這個選項把這些圖片渲染成藍色。複製圖片對記憶體和CPU使用來說都是一項非常昂貴的操作,所以應該儘可能的避免。

Color Immediately  顏色立即更新

通常Core Animation Instruments以每毫秒10次的頻率更新圖層除錯顏色。對某些效果來說,這顯然太慢了。這個選項就可以用來設定每幀都更新(可能會影響到渲染效能,而且會導致幀率測量不準,所以不要一直都設定它)。

Color Misaligned  Images(圖片的不正常縮放)

-這裡會高亮那些被縮放或者拉伸以及沒有正確對齊到畫素邊界的圖片(也就是非整型座標)。這些中的大多數通常都會導致圖片的不正常縮放,如果把一張大圖當縮圖顯示,或者不正確地模糊影像,那麼這個選項將會幫你識別出問題所在。

Color OpenGL Fast Path Blue

 

這個選項會對任何直接使用OpenGL繪製的圖層進行高亮。如果僅僅使用UIKit或者Core Animation的API,那麼不會有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不顯示藍色塊的話就意味著你正在強制CPU渲染額外的紋理,而不是繪製到螢幕。

Flash Updated Regions(Core Graphics繪製的圖層)

-這個選項會對重繪的內容高亮成黃色(也就是任何在軟體層面使用Core Graphics繪製的圖層)。這種繪圖的速度很慢。如果頻繁發生這種情況的話,這意味著有一個隱藏的bug或者說通過增加快取或者使用替代方案會有提升效能的空間。

7、Core Data  核心資料 

 依舊是我們在網上查詢資料,好多資料都是關於如何使用CoreData儲存資料的,即便我們在前面加上Istruments,查到的資料還是關於如何儲存資料的,還好我們另一位同事找到了資料。

Core data也叫檢測核心資料故障的儀器,主要是用於監測讀取、快取未命中、儲存等操作,能直觀顯示是否儲存次數遠超實際需要。以下的instruments工具收集的資料和Core Data應用的事件相關。你可以使用這些instruments工具返回的資訊來評估各種事件對應用效能的影響和來定位潛在問題並修復它。

Core data instrument工具可以執行在單一程式或所有系統當前執行的程式之上。它只會記錄使用Core Data的程式的資料。該instrument工具在實現上使用了DTrace,並可以匯入DTrace指令碼。

321440398-98c57866c9caf224

Core Data Fetches 工具記錄Core Data應用中提取儲存資料的操作。

Core Data Cache Misses 工具記錄快取記憶體未命中導致的故障事件。

Core Data Saves 工具記錄了Core Data應用中儲存的操作。

8、Counters  計數器    

從使用者管理的點事件計數器儀器記錄資訊。它可以記錄從一個過程或系統上執行的所有程式的資訊。

9、Energy Diagnostic  能量診斷   

用於Xcode下的Instruments來分析手機電量消耗的。(必須是真機才有電量)

10、File Activity  檔案活動  

按照介紹this template monitors file and directory activity ,including file open close calls,file permission modifications, directory creation ,file moves ,etc.翻譯過來是:這個模板活動監視器檔案和目錄,包括檔案開啟或者關閉,檔案許可權修改,目錄建立,檔案移動等。

11、GPU Driver 顯示卡驅動程式 

GPU Driver可以測量GPU的利用率,同樣也是一個很好的來判斷和GPU相關動畫效能的指示器。它同樣也提供了類似Core Animtaion那樣顯示FPS的工具

331440398-8569fee98e0ce460

341440398-69ec5a5cb4d0d285

看來這個GPU Driver 還是和Core Animation有關。

12、Leaks 洩漏  

(!!又一個重頭戲來了!!)

首先我們看一看記憶體溢位和記憶體洩漏的區別。

記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。

記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。

memory leak會最終會導致out of memory!

在前面的ALLcations裡面我們提到過記憶體洩漏就是應該釋放而沒有釋放的記憶體。而記憶體洩漏分為兩種:Leaked MemoryAbandoned Memory。前面我們講到了如何找到Abandoned Memory被遺忘的記憶體,現在我們研究的就是Leaked Memory。

351440398-0bd1c8d1995ffa51

發生的方式來分類,記憶體洩漏可以分為4類:

常發性記憶體洩漏。發生記憶體洩漏的程式碼會被多次執行到,每次被執行的時候都會導致一塊記憶體洩漏。

偶發性記憶體洩漏。發生記憶體洩漏的程式碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測記憶體洩漏至關重要。

一次性記憶體洩漏。發生記憶體洩漏的程式碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊僅且一塊記憶體發生洩漏。比如,在類的建構函式中分配記憶體,在解構函式中卻沒有釋放該記憶體,所以記憶體洩漏只會發生一次。

隱式記憶體洩漏。程式在執行過程中不停的分配記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡並沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對於一個伺服器程式,需要執行幾天,幾周甚至幾個月,不及時釋放記憶體也可能導致最終耗盡系統的所有記憶體。所以,我們稱這類記憶體洩漏為隱式記憶體洩漏。

影響:從使用者使用程式的角度來看,記憶體洩漏本身不會產生什麼危害,作為一般的使用者,根本感覺不到記憶體洩漏的存在。真正有危害的是記憶體洩漏的堆積,這會最終消耗盡系統所有的記憶體。從這個角度來說,一次性記憶體洩漏並沒有什麼危害,因為它不會堆積,而隱式記憶體洩漏危害性則非常大,因為較之於常發性和偶發性記憶體洩漏它更難被檢測到。

下邊我們介紹Instruments裡面的Leaked的用法,首先開啟Leaked,跑起工程來,點選要測試的頁面,如果有記憶體洩漏,會出現下圖中的紅色的❌。然後按照後邊的步驟進行修復即可。

361440398-482e80e6ff4390db

371440398-f0760e0a503c8c40

381440398-2a353cef7eb3812d

下圖是對Leaked頁面進一步的理解:

391440398-cfe69945d6a4e955

當然了,關於記憶體洩漏我們還可以用 command +shift +B 的方式進行檢測,這個快捷鍵調起的是記憶體管理器Analyze。

也可以從Product裡面直接開啟

401440398-3337621889b43bcc

關於記憶體管理器Analyze解決問題可以參考這篇文章APP Analyze(靜態分析)也可以參考Analyze問題解決

使用記憶體管理器遇到的問題大概分為:

1、garbage value(垃圾值)

2、never read(分配了空閒記憶體)

3、Null passed to a callee that requires a non-null 1st parameter(Null賦值給非空物件)

4、Potential leak of an object stored into ‘XX'(存在潛在的記憶體洩露)

上述兩篇文章很好的解答了出線這4個問題如何解決,這裡就不贅述了。

關於記憶體洩露還是比較重要的,大家看看,測試一下自己專案裡面的記憶體洩漏,另外如果大家有什麼更好的方法,希望能夠告訴我,謝謝。

使用上述兩種方法(1)Instruments-Leaked (2)記憶體管理器Analyze  來檢查記憶體洩漏,是我們最常用的兩種。

13、Metal System Trace  

金屬系統跟蹤  

翻譯下圖紅框的英文得到:金屬ios系統跟蹤配置檔案的效能應用程式從應用程式通過提供跟蹤資訊,司機和GPU層。(狗屁不通)從網上找Metal System Trace相關的資料也沒有找到。誰知道這是幹啥用的。

411440398-5216651620a01def

14、Network 網路

同樣是翻譯下邊的英文:分析應用程式如何使用TCP / IP和UDP / IP連線使用連線儀器。就是檢查手機網速的。(這個最好是真機)

15、OpenGL ES Analysis  openGL分析  

同樣是翻譯下邊的英文:這個模板的措施和分析OpenGL ES活動檢測OpenGL ES正確性和效能問題也提供瞭解決這些問題的建議。

16、System Trace  系統跟蹤  

該模板提供了系統行為的全面資訊。它顯示執行緒何時排程,並顯示從使用者到系統程式碼的執行緒轉換,通過系統呼叫和記憶體操作。這個模板可以在OS X作業系統和iOS上使用。包含三個模板

Scheduling  Instrument——排程工具

System Calls  Instrument—系統呼叫儀器

VM Tracker   Instrument—–VM跟蹤儀

17、System Usage  系統使用 

這個模板監視一個應用程式和記錄系統的I / O活動相關的檔案,套接字和共享記憶體。這包括輸入,輸出,時間回溯,呼叫樹,乃至每一次響應。該模板只可用於iOS包含一個模板

I/O Activity    Instrument—–I/O活動儀

I/O Activity instrument工具記錄I/O事件:函式呼叫,比如在檔案系統上面的read、write、open、close等操作。你可以使用該instrument工具來啟動和樣本分析單個執行在iOS裝置上面的程式。儘管I/O Activity instrument工具提供了一個呼叫樹的回溯跟蹤檢視,在Mac  OS X上有類似I/O Activity instrument工具的fs_usage實用工具。

18、Time Profiler  時間分析器 

(又一個重頭戲!!!)

用來檢測app中每個方法所用的時間,並且可以排序,並查詢出哪些函式佔用了大量時間。頁面如下:

421440398-419a4530df61be5a

使用Time Profile前有兩點需要注意的地方:

1、一定要使用真機除錯

在開始進行應用程式效能分析的時候,一定要使用真機。因為模擬器執行在Mac上,然而Mac上的CPU往往比iOS裝置要快。相反,Mac上的GPU和iOS裝置的完全不一樣,模擬器不得已要在軟體層面(CPU)模擬裝置的GPU,這意味著GPU相關的操作在模擬器上執行的更慢,尤其是使用CAEAGLLayer來寫一些OpenGL的程式碼時候,這就導致模擬器效能資料和使用者真機使用效能資料相去甚遠

2、應用程式一定要使用釋出配置

在釋出環境打包的時候,編譯器會引入一系列提高效能的優化,例如去掉除錯符號或者移除並重新組織程式碼。另iOS引入一種”Watch Dog”[看門狗]機制,不同的場景下,“看門狗”會監測應用的效能,如果超出了該場景所規定的執行時間,“看門狗”就會強制終結這個應用的程式。開發者可以crashlog看到對應的日誌,但Xcode在除錯配置下會禁用”Watch Dog”

431440398-51fd6c89d63d6e6e

下面解釋了每一個選項對左側列表中資料的顯示起了什麼作用:

Separate by Thread:每個執行緒被單獨考慮。這能讓你知道哪一個執行緒佔用CPU最多。

Invert Call Tree:選中該選項後,呼叫棧會自上至下顯示。這通常是你需要的,因為你想知道CPU花費時間的那個最深的方法。

Hide System Libraries:選中該選項後,只有你自己app中出現的符號會被顯示出來。通常選中該選項是有用的,因為你只關心CPU在你自己的程式碼中的哪一部分花費時間,你沒法對系統庫使用CPU做多少改變。

Flatten Recursion:該選項將每一個呼叫棧中的遞迴函式(呼叫它們自身的函式)視作單一入口,而不是多入口。

Top Functions:選上這一選項讓Instruments將花費在一個函式中的總時間視作在該函式中直接花費的時間加上呼叫的其他函式花費的時間。所以如果函式A呼叫了函式B,那麼函式A花費的總時間被記為A花費的時間加上B花費的時間。這一選項非常有用,因為它能讓你在每次進入呼叫棧時找到花費最長的時間,瞄準你最耗時的方法。

看到網上有人說主執行緒耗時過多進行優化的,有的是網路請求耗時過多進行優化的,有的是UIImage耗時過多進行優化的,總之,可以看到哪個函式耗時多,進而優化,在這裡不由得想起了本文開頭提到的一位網友說的:Instruments的價值在於,它使我們深刻理解我們程式碼的內部運作。誠不欺吾。

總結:效能優化是在所有更能實現完成時要做的事,使用Time Profile工具分析app每個流程的執行情況,發現耗時的地方,合理優化,提升使用者體驗,切記,優化後要做一遍詳細的測試,別修了東牆壞了西牆。

19、Zombies  殭屍  

(最後一個,也是最後一個重頭戲)

翻譯英文:專注於檢測過度釋放的“殭屍”物件。還提供了資料物件分配的類以及所有活動分配記憶體地址的歷史。

這裡我們可以看到一個詞語叫“over-release”,過度釋放。我們在專案中見到最多的就是“EXC_BAD_ACCESS”或者是這樣的:Thread 1: Program received signal:”EXC_BAD_ACCESS“,這就是訪問了被釋放的記憶體地址造成的。過度釋放,是對同一個物件釋放了過多的次數,其實當引用計數降到0時,物件佔用的記憶體已經被釋放掉,此時指向原物件的指標就成了“懸垂指標”,如若再對其進行任何方法的呼叫,(原則上)都會直接crash(然而由於某些特殊的情況,不會馬上crash)。過度釋放簡單的說就是對release的物件再release,就是過度釋放。

我們需要知道這幾個概念:

1、記憶體洩漏:物件使用完沒有釋放,導致記憶體浪費。

2、殭屍物件:已經被銷燬的物件(不能再使用的物件)

3、野指標:指向殭屍物件(不可用記憶體)的指標。給野指標發訊息會報EXC_BAD_ACCECC錯誤。

4、空指標:沒有指向儲存空間的指標(裡面存的是nil,也就是0)。在oc中使用空指標調中方法不會報錯。

注意:為了避免野指標錯誤的常見方法:在物件被銷燬之後,將指向物件的指標變為空指標。

對於過度釋放的問題,可以直接使用Zombie,當過度釋放發生時會立即停在發生問題的位置,同時結合記憶體分配釋放歷史和呼叫棧,可以發現問題。至於上文提到的不會crash的原因,其實有很多,比如:

物件記憶體釋放時,所用記憶體並沒有完全被擦除,仍有舊物件部分資料可用

原記憶體位置被寫入同類或同樣結構的資料

我們將殭屍物件“復活”的目的:殭屍物件就是讓已經釋放了的物件重新復活,便於除錯;是為了讓已經釋放了的物件在被再次訪問時能夠輸出一些錯誤資訊。其實這裡的“復活”並不是真的復活,而是強行不死:這麼說吧 相當於 他的RC=0的時候 系統再強行讓他RC=1,順便打上一個標記 zoom,等到你去掉那個溝以後 系統會把帶有標記zoom的物件RC=0。

可以參考IOS效能調優系列:使用Zombies動態分析記憶體中的殭屍物件

可以參考iOS 遇到EXC_BAD_ACCESS解決方法

可以參考ios 除錯技巧收藏 一 解決EXC_BAD_ACCESS錯誤的一種方法–NSZombieEnabled

可以參考野指標與殭屍物件

下邊是Instruments裡面的Zombies的用法:

441440398-b7c3198642a0f036

接下來進行設定,在Launch  Configuration中勾選Record reference counts和Enable NSZombie detection。其中Recordreference counts是顯示引用計數,Enable NSZombie detection是能夠檢測殭屍物件。

451440398-e5b737becf5a687c

這樣在程式執行的時候,如果發現殭屍物件它就會彈出一個對話方塊,點選其中“→”按鈕,在螢幕的下方會顯示殭屍物件的詳細資訊,下圖可以看到殭屍物件的引用計數變化情況。

461440398-3ee586361ed60de0

注意:Zombies模版在使用的時候會導致記憶體的飆升,這是因為所有被釋放的物件被殭屍物件取代,並未真的釋放掉,在結束Zombies時會釋放,這是預知行為,這就意味著instrument裡的其它工具和Zombies是不能同時使用的,Zombies會導致其它的資料不準。包括leaks,你也不應該把它加到Zombies模版中,即使這麼做了結果也沒什麼意義。對於iOS應用來說,在用Zombies模版時使用iOS模擬器比真機要好。

另外XCode也提供了手動設定NSZombieEnabled環境變數的方法,不過設定NSZombieEnabled為True後,會導致記憶體佔用的增長,同時會影響Leaks工具的除錯,這是因為設定NSZombieEnabled會用殭屍物件來代替已釋放物件。

點選Product選單Edit Scheme開啟該頁面,然後勾選Enable Zombie Objects核取方塊:

471440398-d1201645e5c89487

最後提醒的是NSZombieEnabled只能在除錯的時候使用,千萬不要忘記在產品釋出的時候去掉,因為NSZombieEnabled不會真正去釋放dealloc物件的記憶體,一直開啟的話,該死去的物件會一直存在,後果可想而知,自重!

好了,結束了。還是那句話:Instruments的價值在於,它使我們深刻理解我們程式碼的內部運作

最後,哪裡不對的地方可以給我留言,我會及時改進的,謝謝大家。

相關文章