1. 程式人生 > >Instruments之Leaks學習

Instruments之Leaks學習

man ons -1 創建對象 ack 聲明 call 銷毀 原因

前言: 本篇文章,在於學習,我把別人的一些感覺好的文章匯總成了一篇,親自實現了一下,留用於今後學習資料。

文章脈絡:
技術分享
文章脈絡:

一、內存優化

簡介:
Objective_C 有3種內存管理方法, 它們分別是

- MRR (Manual Retain Release, 手動保持釋放)- ARC(Automatic Reference Counting, 自動引用計數)- GC(Garbage Collection, 垃圾收集)
  • 1>MRR
    ① 也稱為 MRC(Manual Reference Counting, 手動引用計數)
    ② 由程序員自己負責管理對象生命周期,負責對象的創建和銷毀.

  • 2>ARC
    ① 采用和 MRR 一樣的內存引用計數管理方法。
    ② 在編譯時會在適合的位置插入對象內存釋放, (如 release, autorelease, 和 retain 等),
    ③ 程序員不用關心對象釋放的問題, 蘋果推薦在新項目中使用 ARC, 但在 iOS5之前的系統中不能采用 ARC.

  • 3>GC
    ① 在Objective_C2.0之後, 內存管理出現了類似於 Java 和 C#的內存垃圾收集技術, 但是垃圾收集與 ARC 一直運行, 垃圾收集是後臺有一個線程負責檢查已經不再使用的對象,然後釋放之.
    ② 由於後臺有一個線程一直運行, 一次會嚴重影響性能, 這也是 Java 和 C#程序的運行速度無法超越 C++的主要原因.
    ③ GC 技術不能應用於 iOS 開發, 只能應用於Mac OS X 開發.

    小結:
    從上面的介紹可知:

  • ① iOS 采用 MRR 和 ARC 這兩種方式, ARC 是蘋果推薦的方式.

  • ② MRR 方式相對比較原始, 對於程序員的能力要求很高, 但是它很靈活, 方便, 很不容易駕馭好.

二、內存泄露

1> 什莫是內存泄露?
內存泄露指當一個對象或變量在使用完成後沒有釋放掉, 這個對象一直占用著這部分內存, 直到應用停止.

2> 這種沒有 釋放掉的對象 多了會發生什麽呢?
如果這種對象過多,內存就會耗盡,其他應用就無法運行.

3> 在哪裏比較普遍?
這個問題在 C++, C 和 Objective-C的 MRR 中是比較普遍的問題.

4> 理論與實際?
從理論上講, 內存泄露是由對象或變量沒有釋放引起的, 但實踐證明並非所有的未釋放的對象或變量都會導致內存泄露, 這與硬件環境和操作系統系統環境有關。

5> 我們該怎麽辦呢?
我們需要檢測工具幫助我們找到這些"泄漏點".

6> 為什麽要測試代碼的內存泄露?
內存的泄露導致我們軟件在運行過程中占用越來越多的內存,占有資源卻又得不到及時清理,會導致我們程序效率越來越低,反應慢,會影響我們用戶體驗,失去市場的競爭能力.

三、查找泄漏點 (兩種工具)

在 Xcode 中, 共提供了兩種工具幫助查找泄漏點
1 > Analyze

- 學 名:  靜態分析工具- 查 找:  可以通過 Product ->Analyze 菜單項啟動- 快捷鍵:  CMD+shift +b.- Analyze主要分析以下四種問題:
  1) 邏輯錯誤:訪問空指針或未初始化的變量等;
  2) 內存管理錯誤:如內存泄漏等;
  3) 聲明錯誤:從未使用過的變量;
  4) Api調用錯誤:未包含使用的庫和框架。

2 >Instruments

- 學 名:   動態分析工具- 查 找:   Product ->Profile 菜單項啟動- 快捷鍵:  CMD + i.- 簡 介:它有很多跟蹤模塊可以動態分析和跟蹤內存, CPU 和文件系統.
四、兩種工具查找漏點版面的介紹

1 > 結合使用-思路分析:
先使用 Analyze 靜態分析查找可疑泄漏點, 再用Instruments動態分析中的 Leaks 和 Allocations 跟蹤模板進行動態跟蹤分析, 確認這些點是否泄漏, 或者是否有新的泄漏點出現等.

2 > 使用 靜態檢測內存泄漏工具 Analyze
在 Analyze 靜態分析結果中, 凡是有圖標

技術分享

分析結果圖標


出現的行都是工具發現的疑似泄露點.

技術分享

疑似泄漏點所在行


點擊疑似泄漏點行末尾的分叉圖標,會展開分析結果:

技術分享

展開分析結果


檢測完成時的效果圖如下

技術分享

效果圖


小結:
這裏使用 Analyze 靜態分析查找出來的泄漏點,稱之為"可疑泄漏點".之所以稱之為"可疑泄漏點",是因為這些點未必一定泄露,確認這些點是否泄露, 還要通過 Instruments 動態分析工具的 Leaks 和 Allocations 跟蹤模板. Analyze 靜態分析只是一個理論上的預測過程.

3 > 動態監測Instruments的Leaks
1) CMD + i 打開

技術分享

打開


2) 打開界面的介紹:

技術分享

界面的介紹


在 instruments 中,雖然選擇了 Leaks 模板,但默認情況下也會添加 Allocations 模板.基本上凡是內存分析都會使用 Allocations 模板, 它可以監控內存分布情況。

① 選中 Allocations 模板,(圖1區域),右邊的3區域會顯示隨著時間的變化內存使用的折線圖,同時在4區域會顯示內存使用的詳細信息,以及對象分配情況.

② 點擊 Leaks 模板(圖中2區域), 可以查看內存泄露情況。如果在3區域有 紅X 出現, 則有內存泄露, 4區域則會顯示泄露的對象.

3) 打用leaks進行監測:
點擊泄露對象可以在(下圖)看到它們的內存地址, 占用字節, 所屬框架和響應方法等信息.打開擴展視圖, 可以看到右邊的跟蹤堆棧信息

技術分享

leaks進行監測


4) 監測結果的分析:

技術分享

監測結果的分析


4 > Allocations—內存分配版面的介紹
Allocations是檢測程序運行過程中的內存分配情況的,也需要同時運行著程序。界面情況如下:

技術分享

Allocations

技術分享

Allocations


雙擊某一個方法,同樣會跳轉到代碼裏,會有每一句代碼對應的內存分配情況,根據這些信息,可以對程序裏不同代碼的內存占用情況有一些認識,並進行針對性的優化。

五、具體使用

1>.Allocations紀錄了內存分配,用來優化內存使用的
2>.Leaks用來分析內存泄漏。ARC中引起的內存泄漏原因就是引用環。

第一步
先選擇Leaks和Leaks by Backtrace.這裏可以看到那些對象內存泄漏了,泄漏了多少,這個就是簡單看看,沒有太多調試意義。

技術分享

泄漏了多少


第二步
然後看看Call Tree,因為Call Tree會給我們大概的位置,有時候會給我們精確的位置,不過要看運氣了。
然後,再右面選擇Invert Call Tree和Hide System Library

技術分享

Call Tree


然後雙擊 上文圖片中的任意一行,就會跳到代碼處內存泄漏的地方(事實上,到這步,很多內存泄漏的問題都會被發現),當然也有一些泄露還是看不出來的.
第三步
然後我們選擇對ARC調試很有用的一個部分Circles & Roots,通過這個我們可以看到詳細的ARC引用計數過程。然後,我們看到如下圖

小的紅色矩形點擊可以看到引用計數的詳細信息(ARC 就是自動引用計數,計數為0,則對象會被釋放)

大的紅色矩形可以繪制對象引用環的圖,這裏如果是我們自己的東西,就能看出來各個對象之間的引用.

技術分享

Circles & Roots


如果這裏沒有引用環的圖. 首先我們找一下我們自定義的對象,正常完成任務這個對就應該釋放的. 為了確認這個對象有沒有釋放, 可以重寫 dealloc 方法, 在此方法中 log 釋放信號, 看看是否被釋放.

如果這裏就是沒有釋放,我們可以點擊這兒對象後的箭頭詳細的看下, 這個對象的引用計數變化如圖.

 - All 表示所有的引用計數變化
 - Unpaired表示那些為成對的變化``(成對就是leaks識別出了對應的+1,-1)
 - By Group會把相關的變化分成一組, 
 - ByTime會按照順序列出引用計數變化

技術分享


我們選擇Unpaired 和 ByGroup,看到如圖

技術分享


按照順序看(最左邊的標號)
4 這裏,引用計數是一,這是正確的,因為到這裏正常就是應該是OperationQueue保存一個Operation的引用。 於是,我們把正常的劃掉

技術分享


再繼續看,download start 標號6和8是對應的,繼續排除問題出現在這裏(當然問題不可能出現在這裏,這是系統的API,一定會釋放,就是簡單教大家如何看)

技術分享


再看看,+1的還剩下標號7 和 11,7 是正常的為Operation分配線程,應當會+1,而11就是我們的問題所在了(大部分Delegate都不會使引用+1)。 我們再看下文檔

- > @property(readonly, retain) id< NSURLSessionDelegate > delegate

原來這個代理是retain啊,不是assign或者weak。所以形成了這樣的引用環。

技術分享


那麽怎麽辦呢?有問題下看文檔,我們看到圖片中引起引用計數加一的是

  - >  + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id)delegate delegateQueue:(NSOperationQueue *)queue:

看下文檔,發現了這個地方

技術分享

  • 於是,我們要手動的去斷開強引用,於是,我們手動去斷開

      -(void)setOperationFinished
      {
       [self.session invalidateAndCancel];
       }

    再運行下看看,能夠正常的Dealloc了.

總結:其實大多數問題在雙擊上文的代碼部分就可以解決了,少數問題需要詳細的分析ARC引用過程。

建議
如果我們未發現表示內存泄露的紅 X, 但是我們想進一步評估某個對象對於內存的應用, 可以看看 Allocations 模板的折線圖. 反復執行從創建對象 -> 銷毀對象 這個過程, 如果總占用內存數會隨之增加, 這說明這個對象沒有釋放, 有些時候雖然占用的內存不是很嚴重, 但是也會增加占用內存, 因此必須釋放這個對象.

提示:有些情況下, 對象沒有釋放是無法檢測到的,反復測試內存占用也沒有明顯的增加, 這時最好在配置比較低的設備上測試一下, 如果問題依然, 可以不用釋放對象. 但是從編程習慣上講, 我們應該釋放該對象.

事實上,內存泄露是及其復雜的問題, 工具使用是一方面, 經驗是另一方面. 提高經驗, 然後借助工具才能解決內存泄露的根本.

擴展小知識
1.什莫是內存溢出 out of memory ?
是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。

Instruments之Leaks學習