動(dòng)態(tài)內(nèi)存泄露分析

動(dòng)態(tài)內(nèi)存泄露分析

時(shí)間:2017年9月29日 周五

1、內(nèi)存泄露
通常說的內(nèi)存泄漏是指堆內(nèi)存的泄漏运授。
堆內(nèi)存是指程序從堆中分配的烤惊、大小任意的(內(nèi)存塊的大小可以在程序運(yùn)行期決定)乔煞,使用完后必須顯式釋放的內(nèi)存。創(chuàng)建對(duì)象時(shí)撕氧,用alloc瘤缩、new、copy等方法從堆中分配到一塊內(nèi)存伦泥,使用完后剥啤,程序必須負(fù)責(zé)相應(yīng)的調(diào)用free或delete釋放該內(nèi)存塊,否則不脯,這塊內(nèi)存就不能被再次使用府怯,我們就說這塊內(nèi)存泄漏了。
如果程序運(yùn)行時(shí)一直分配內(nèi)存而不及時(shí)釋放無用的內(nèi)存防楷,程序占用的內(nèi)存越來越大牺丙,直到把系統(tǒng)分配給該APP的內(nèi)存消耗殫盡,程序因無內(nèi)存可用導(dǎo)致崩潰复局。
可能引起的問題:
1)內(nèi)存消耗殆盡的時(shí)候冲簿,程序會(huì)因沒有內(nèi)存被殺死,即crash亿昏;
2)當(dāng)內(nèi)存快要用完的時(shí)候峦剔,會(huì)非常的卡頓;
3)如果是ViewController沒有釋放掉角钩,引起的內(nèi)存泄露吝沫,還會(huì)引起其他很多問題,尤其是和通知相關(guān)的递礼。沒有被釋放掉的ViewController還能接收通知惨险,還會(huì)執(zhí)行相關(guān)的動(dòng)作,所以會(huì)引起各種各樣的異常情況的發(fā)生脊髓。
從iOS5開始apple采用ARC機(jī)制辫愉,不需要我們對(duì)內(nèi)存進(jìn)行管理了。但在實(shí)際開發(fā)過程中将硝,不可避免的出現(xiàn)內(nèi)存泄露的情況恭朗。

2、xcode自帶的工具
Instrument中的Leaks袋哼、Allocations工具可以幫助我們進(jìn)行內(nèi)存泄露的排查冀墨,但它們存在不足闸衫。
在 ARC 時(shí)代更常見的內(nèi)存泄露是循環(huán)引用導(dǎo)致的 Abandoned memory涛贯,Leaks 工具查不出這類內(nèi)存泄露,應(yīng)用有限蔚出;
Instrument 的 Allocations 檢測(cè)出 Abandoned memory弟翘,但是用起來不方便虫腋,需要一個(gè)一個(gè)地重現(xiàn)場(chǎng)景,且無法及時(shí)的知道泄露稀余,比較繁瑣悦冀。總之睛琳,一句話就是盒蟆,不好使。

3师骗、開源框架
在 GitHub 上有一些內(nèi)存泄露檢測(cè)相關(guān)的項(xiàng)目历等,例如 HeapInspector-for-iOS 和 MSLeakHunter。
1)HeapInspector-for-iOS
HeapInspector-for-iOS 可以說是 Allocations 的改進(jìn)辟癌。它通過 hook 掉 alloc寒屯,dealloc,retain黍少,release 等方法寡夹,來記錄對(duì)象的生命周期。具體的檢測(cè)內(nèi)存泄露的方法和原理厂置,與 Instrument 的 Allocations 一致菩掏。然而它跟 Allocations 一樣,存在的問題是农渊,你需要一個(gè)個(gè)場(chǎng)景去重復(fù)的操作患蹂,還有檢測(cè)不及時(shí)。
2)MSLeakHunter
MSLeakHunter 就簡(jiǎn)單得多砸紊,它只檢測(cè) UIViewController 和 UIView传于,通過 hook 掉 UIViewController 的 -viewDidDisappear: 方法,并認(rèn)為 -viewDidDisappear: 后醉顽,UIViewController 將很快被釋放沼溜,如果 UIViewController 沒有被釋放,則打個(gè)建議日志游添。這種做法其實(shí)不是很好系草,-viewDidDisappear: 被調(diào)用可能是因?yàn)橛?push 進(jìn)來一個(gè)新的 ViewController,把當(dāng)前的 ViewController 擋住了唆涝,所以可能有很多錯(cuò)誤的建議找都,需要結(jié)合你實(shí)際的操作去具體地分析日志。

4廊酣、動(dòng)態(tài)檢測(cè)工具:MLeaksFinder

1)MLeaksFinder檢測(cè)內(nèi)存泄露原理
MLeaksFinder用于檢測(cè)viewController能耻、view是否存在內(nèi)存泄露的情況。
當(dāng)一個(gè) UIViewController 被 pop 或 dismiss 后,該 UIViewController 包括它的 view晓猛,view 的 subviews 等等將很快被釋放(除非你把它設(shè)計(jì)成單例饿幅,或者持有它的強(qiáng)引用,但一般很少這樣做)戒职。于是栗恩,我們只需在一個(gè) ViewController 被 pop 或 dismiss 一小段時(shí)間后,看看該 UIViewController洪燥,它的 view磕秤,view 的 subviews 等等是否還存在。
具體的方法是捧韵,為基類 NSObject 添加一個(gè)方法 -willDealloc 方法亲澡,該方法的作用是,先用一個(gè)弱指針指向 self纫版,并在一小段時(shí)間(3秒)后床绪,通過這個(gè)弱指針調(diào)用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中斷言其弊。

- (BOOL)willDealloc {
    __weak id weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        __strong id strongSelf = weakSelf;
        [strongSelf assertNotDealloc];
    });   
    return YES;
}
- (void)assertNotDealloc {
    if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]) {
        return;
    }
    [MLeakedObjectProxy addLeakedObject:self];
}

這樣癞己,當(dāng)我們認(rèn)為某個(gè)對(duì)象應(yīng)該要被釋放了,在釋放前調(diào)用這個(gè)方法梭伐,如果3秒后它被釋放成功痹雅,weakSelf 就指向 nil,不會(huì)調(diào)用到 -assertNotDealloc 方法糊识,也就不會(huì)中斷言绩社,如果它沒被釋放(泄露了),-assertNotDealloc 就會(huì)被調(diào)用中斷言赂苗。這樣愉耙,當(dāng)一個(gè) UIViewController 被 pop 或 dismiss 時(shí)(我們認(rèn)為它應(yīng)該要被釋放了),我們遍歷該 UIViewController 上的所有 view拌滋,依次調(diào) -willDealloc朴沿,若3秒后沒被釋放,就會(huì)中斷言败砂,并且調(diào)用[MLeakedObjectProxy addLeakedObject:self];該方法會(huì)獲取內(nèi)存泄露的信息赌渣,并彈框顯示。

2)特點(diǎn):
A昌犹、不入侵代碼
直接拖進(jìn)工程坚芜,配置設(shè)置即可使用。
使用了 AOP 技術(shù)斜姥,hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法鸿竖。(AOP技術(shù)路操,可以閱讀”漫談iOS AOP編程之路“)
B、可以建立例外
對(duì)于有些 ViewController千贯,在被 pop 或 dismiss 后,不會(huì)被釋放(比如單例)搞坝,因此需要提供機(jī)制讓開發(fā)者指定哪個(gè)對(duì)象不會(huì)被釋放搔谴,這里可以通過重載上面的 -willDealloc 方法,直接 return NO 即可桩撮。
C敦第、手動(dòng)擴(kuò)展:MLCheck()
MLeaksFinder目前只檢測(cè) ViewController 跟 View 對(duì)象。為此店量,MLeaksFinder 提供了一個(gè)手動(dòng)擴(kuò)展的機(jī)制芜果,你可以從 UIViewController 跟 UIView 出發(fā),去檢測(cè)其它類型的對(duì)象的內(nèi)存泄露融师。如下所示右钾,我們可以檢測(cè) UIViewController 底下的 View Model:

- (BOOL)willDealloc {
    if (![super willDealloc]) {
        return NO;
    }
    MLCheck(self.viewModel);
    return YES;
}

3)MLeaksFinder的增強(qiáng):FBRetainCycleDetector
MLeaksFinder做到的是:告訴我們,viewController旱爆、view是否存在內(nèi)存泄露舀射。
MLeaksFinder不能告訴我們,當(dāng)viewController怀伦、view發(fā)生內(nèi)存泄露時(shí)脆烟,是在什么地方出現(xiàn)的,不能提供內(nèi)存泄露的詳細(xì)信息房待。
因此邢羔,我們接入了一個(gè)檢測(cè)循環(huán)引用的框架FBRetainCycleDetector,它是Facebook的一個(gè)開源工具桑孩,專門用于檢測(cè)對(duì)象的引用情況拜鹤,并輸出存在引用循環(huán)中的各對(duì)象和引用。
當(dāng)MLeaksFinder檢測(cè)到內(nèi)存泄露時(shí)流椒,將存在內(nèi)存泄露的對(duì)象傳參給FBRetainCycleDetector署惯,F(xiàn)BRetainCycleDetector檢測(cè)該對(duì)象的引用情況,并彈框顯示檢測(cè)結(jié)果镣隶。
在MLeaksFinder.h文件中有個(gè)開關(guān)控制极谊,是否開啟FBRetainCycleDetector

#define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 1

5、下面用一個(gè)例子來介紹它們的使用

1)創(chuàng)建GGMLViewController安岂,給它制造一個(gè)內(nèi)存泄露:


2)運(yùn)行工程
點(diǎn)擊測(cè)試工程中的”MLeaksFinder使用“



彈出新的ViewController轻猖,一直點(diǎn)擊按鈕
action按鈕即可,點(diǎn)到不能再?gòu)棾鲂碌腣iewController后域那,就點(diǎn)擊back咙边,回退到首頁(yè)猜煮。


等待3秒時(shí)間,彈出循環(huán)引用的提示框


點(diǎn)擊”定位“败许,彈出相關(guān)循環(huán)引用的信息王带,根據(jù)這信息,我們可以清楚的知道哪里出現(xiàn)了問題市殷。


Demo地址:https://github.com/lignpeng/testDemo

注意:在使用MLeaksFinder時(shí)愕撰,建議把All Exceptions斷點(diǎn)禁掉,避免出現(xiàn)些沒必要的中斷醋寝,這種中斷是在FBRetainCycleDetector框架出現(xiàn)的搞挣,但不影響使用。

引用文章:
1)MLeaksFinder:精準(zhǔn)iOS內(nèi)存泄露工具: http://wereadteam.github.io/2016/02/22/MLeaksFinder/
2)使用FBRetainCycleDetector檢測(cè)引用循環(huán):http://www.reibang.com/p/2c7a7c53c91a
3)漫談iOS AOP編程之路:http://www.reibang.com/p/addd4eac54ed
4)iOS內(nèi)存泄露個(gè)人經(jīng)驗(yàn):http://www.reibang.com/p/10254b4b19f3
5)iOS 測(cè)試工程Demo:http://www.reibang.com/p/4580a370a4d3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末音羞,一起剝皮案震驚了整個(gè)濱河市囱桨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗅绰,老刑警劉巖舍肠,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窘面,居然都是意外死亡貌夕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門民镜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啡专,“玉大人,你說我怎么就攤上這事制圈∶峭” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鲸鹦,是天一觀的道長(zhǎng)慧库。 經(jīng)常有香客問我,道長(zhǎng)馋嗜,這世上最難降的妖魔是什么齐板? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮葛菇,結(jié)果婚禮上甘磨,老公的妹妹穿的比我還像新娘。我一直安慰自己眯停,他們只是感情好济舆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著莺债,像睡著了一般滋觉。 火紅的嫁衣襯著肌膚如雪签夭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天椎侠,我揣著相機(jī)與錄音第租,去河邊找鬼。 笑死我纪,一個(gè)胖子當(dāng)著我的面吹牛慎宾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宣羊,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼汰蜘!你這毒婦竟也來了仇冯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤族操,失蹤者是張志新(化名)和其女友劉穎苛坚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體色难,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泼舱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枷莉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娇昙。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笤妙,靈堂內(nèi)的尸體忽然破棺而出冒掌,到底是詐尸還是另有隱情,我是刑警寧澤蹲盘,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布股毫,位于F島的核電站,受9級(jí)特大地震影響召衔,放射性物質(zhì)發(fā)生泄漏铃诬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一苍凛、第九天 我趴在偏房一處隱蔽的房頂上張望趣席。 院中可真熱鬧,春花似錦醇蝴、人聲如沸吩坝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钉寝。三九已至弧呐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嵌纲,已是汗流浹背俘枫。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逮走,地道東北人鸠蚪。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像师溅,于是被迫代替她去往敵國(guó)和親茅信。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容