前言: 本篇文章,在於學習,我把別人的一些感覺好的文章匯總成了一篇,親自實現了一下,留用於今後學習資料。
文章脈絡:
文章脈絡:
簡介:
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才能存下的數,那就是內存溢出。學習文章出處:
1. iOS_ 性能優化_內存優化_Leaks工具的使用
2. [學習筆記]_ios內存優化leaks以及timeProfiler工具的使用
http://www.jianshu.com/users/b47caf28043c/latest_articles