漫談內(nèi)存泄漏

前言

最近看了同事整理的一份與內(nèi)存泄漏相關(guān)思維導(dǎo)圖。突然想從內(nèi)存泄漏的角度探討一下與內(nèi)存相關(guān)的話題。
什么是內(nèi)存泄漏硅急?然而我又問自己一個(gè)問題, malloc 的內(nèi)存到底是什么佳遂?

什么是內(nèi)存

在計(jì)算機(jī)系統(tǒng)中营袜,我們談?wù)摰?strong>內(nèi)存通常是指DRAM

虛擬內(nèi)存空間

而當(dāng)我們程序在系統(tǒng)上運(yùn)行起來時(shí)丑罪,操作系統(tǒng)為我們提供了一個(gè)假象荚板,程序看起來是獨(dú)占使用處理器、主存和I/O設(shè)備的吩屹。這種假象是通過進(jìn)程的概念來實(shí)現(xiàn)的跪另。

虛擬內(nèi)存,也為進(jìn)程提供一個(gè)假象煤搜,每個(gè)進(jìn)程都獨(dú)占地使用主存免绿。每個(gè)進(jìn)程看到的內(nèi)存都是一致的,稱為虛擬內(nèi)存空間擦盾。

虛擬內(nèi)存的能力:

  • 使主存中只保存活動(dòng)區(qū)域针姿,根據(jù)需要在磁盤和主存之間來回傳輸數(shù)據(jù)。
  • 為每個(gè)內(nèi)存提供一致的地址空間厌衙。
  • 保護(hù)了每個(gè)進(jìn)程的地址空間不被其他進(jìn)程破壞。
一個(gè) Linux 進(jìn)程的虛擬內(nèi)存.png

上圖是一個(gè) Linux 進(jìn)程的虛擬內(nèi)存绞绒。也就是說我們平時(shí) malloc 得到的內(nèi)存地址婶希,其實(shí)是虛擬內(nèi)存的地址。

動(dòng)態(tài)內(nèi)存分配

其實(shí)系統(tǒng)為我們提供了 mmapmunmap 函數(shù)來創(chuàng)建和刪除虛擬內(nèi)存的區(qū)域蓬衡。但很多時(shí)候直到程序?qū)嶋H運(yùn)行才知道某些數(shù)據(jù)結(jié)構(gòu)的大小喻杈。所以就有了動(dòng)態(tài)內(nèi)存分配器。

動(dòng)態(tài)內(nèi)存分配器有兩種基本類型:

  • 顯式分配器狰晚,需要顯式釋放筒饰。例如 C 和 C++ 。
  • 隱式分配器壁晒,分配器檢測已分配何時(shí)不再被程序所使用瓷们,那么就釋放這個(gè)塊。例如 Lisp、Java 等谬晕。

什么是內(nèi)存泄漏

內(nèi)存泄漏是常見的內(nèi)存錯(cuò)誤之一碘裕。我們知道 malloc 其實(shí)是從虛擬內(nèi)存空間的堆中申請空閑的地址的。然而內(nèi)存空間是有限的攒钳。程序在運(yùn)行中 malloc 出來的內(nèi)存空間使用完后帮孔,沒有被 free 掉,這樣我們就稱之為內(nèi)存泄漏不撑。

ARC 機(jī)制

iOS 上文兢,不論是 Objective-C 還是 Swift 都是使用引用計(jì)數(shù)式的內(nèi)存管理方式。
ARC 就是 Automatic Reference Counting焕檬, 其實(shí) ARC 很簡單姆坚,我們只需要弄清楚對象之間的持有關(guān)系。

舉個(gè)簡單的例子:

場景一

self.textField.text = @"Sim";

下圖簡單的描述了揩页,這段代碼對象之間的持有關(guān)系旷偿。 self.textField.text 持有著 @"Sim"@"Sim" 對象的 retainCount = 1爆侣。

1.png
self.textField.text = @"SimCai";

這時(shí)萍程,對象 @"Sim" 不再被 self.textField.text 持有,所以 @"Sim" 對象的 retainCount = 0兔仰,對象會(huì)被釋放掉茫负。

2.png

場景二

self.textField.text = @"Sim";
NSString *firstName = self.textField.text;

這段代碼,@"Sim" 對象被 firstNameself.textField.text 同時(shí)持有乎赴。所以 @"Sim" 對象的 retainCount = 2 忍法。

3.png
self.textField.text = @"SimCai";

這時(shí) self.textField.text 不再持有 @"Sim" 對象,但是 firstName 依然持有 @"Sim" 榕吼,所以 @"Sim" 的 retainCount = 1 饿序,不會(huì)被釋放。

4.png

直到 firstName 也不再持有 @"Sim" 對象羹蚣,@"Sim" 才會(huì)被釋放原探。

5.png

循環(huán)引用

ARC 整套機(jī)制看起來很簡單,但會(huì)不會(huì)有什么特例顽素,會(huì)造成內(nèi)存無法被正常釋放呢咽弦?
有,循環(huán)引用胁出。

ViewController 持有 TableView型型,同時(shí) TableView 也持有 ViewController 。 他們相互有引用關(guān)系全蝶。這就是循環(huán)引用闹蒜。

6.png

為了打破這種引用的循環(huán)寺枉。我們可以通過 weak (弱引用) 來解決這個(gè)問題。

一般情況下嫂用,ViewController 是會(huì)被個(gè) UINavigationController 所持有型凳。如果 TableView 也持有 ViewController ,這時(shí) ViewController 的 retainCount = 2嘱函。

而我們對 self.vc 使用了 weak 后甘畅,self.vc = ViewController ,這樣的操作往弓,不再會(huì)導(dǎo)致 retainCount 加1 疏唾。 這時(shí) ViewController 依然還是 retainCount = 1。

7.png

而當(dāng) ViewController 被釋放后 self.vc 建會(huì)指向 nil 函似。當(dāng)然在這個(gè)例子槐脏,在 ViewController 釋放后,TableView 自然也會(huì)別釋放撇寞。

8.png

但在一些場景下使用 weak 需要比較注意的顿天。例如,一個(gè)全局的定時(shí)器蔑担,如果持有了 ViewController 是弱引用牌废。 那當(dāng) ViewController 被釋放后,定時(shí)器再去訪問 ViewController 就將引起 crash 啤握。

常見的內(nèi)存泄漏場景

  • 使用時(shí) block (需要格外小心)
  • NSTimer 沒有銷毀
  • KVO 沒有移除
  • NSNotification 沒有移除

當(dāng)了解了 ARC 后鸟缕,再我看來這些本質(zhì)都是循環(huán)引用問題(當(dāng)然還有一些 CF 的API,還是需要手動(dòng)內(nèi)存管理的)排抬。

  • block 會(huì)捕獲變量
  • NSTimer 需要持有對象懂从,進(jìn)行通知回調(diào)
  • KVO 需要持有對象,進(jìn)行通知回調(diào)
  • NSNotification 需要持有對象蹲蒲,進(jìn)行通知回調(diào)

所以這些操作都容易造成內(nèi)存泄漏番甩。

要避免內(nèi)存泄漏,更重要的是需要了解 ARC 的機(jī)制届搁。實(shí)際上对室,可能造成內(nèi)存泄漏的場景還有很多。

總結(jié)

  • malloc 得到的內(nèi)存地址咖祭,實(shí)際是虛擬內(nèi)存空間中的堆地址。并不是實(shí)際的物理地址蔫骂。
  • 內(nèi)存泄漏是指么翰,內(nèi)存資源沒用了,但內(nèi)存資源沒被 free辽旋。(用完了浩嫌,就別占著坑)
  • 在 iOS ARC 時(shí)代檐迟,大部分內(nèi)存泄漏問題,是由循環(huán)引用造成的码耐。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末追迟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子骚腥,更是在濱河造成了極大的恐慌敦间,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件束铭,死亡現(xiàn)場離奇詭異廓块,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)契沫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門带猴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人懈万,你說我怎么就攤上這事拴清。” “怎么了会通?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵口予,是天一觀的道長。 經(jīng)常有香客問我渴语,道長苹威,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任驾凶,我火速辦了婚禮牙甫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘调违。我一直安慰自己窟哺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布技肩。 她就那樣靜靜地躺著且轨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虚婿。 梳的紋絲不亂的頭發(fā)上旋奢,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音然痊,去河邊找鬼至朗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛剧浸,可吹牛的內(nèi)容都是我干的锹引。 我是一名探鬼主播矗钟,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嫌变!你這毒婦竟也來了吨艇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤腾啥,失蹤者是張志新(化名)和其女友劉穎东涡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碑宴,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡软啼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了延柠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祸挪。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贞间,靈堂內(nèi)的尸體忽然破棺而出贿条,到底是詐尸還是另有隱情,我是刑警寧澤增热,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布整以,位于F島的核電站,受9級特大地震影響峻仇,放射性物質(zhì)發(fā)生泄漏公黑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一摄咆、第九天 我趴在偏房一處隱蔽的房頂上張望凡蚜。 院中可真熱鬧,春花似錦吭从、人聲如沸朝蜘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谱醇。三九已至,卻和暖如春步做,著一層夾襖步出監(jiān)牢的瞬間副渴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工全度, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煮剧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓讼载,卻偏偏與公主長得像轿秧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子咨堤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

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

  • 內(nèi)存管理 簡述OC中內(nèi)存管理機(jī)制菇篡。與retain配對使用的方法是dealloc還是release,為什么一喘?需要與a...
    丶逐漸閱讀 1,948評論 1 16
  • 1.1 什么是自動(dòng)引用計(jì)數(shù) 概念:在 LLVM 編譯器中設(shè)置 ARC(Automaitc Reference Co...
    __silhouette閱讀 5,082評論 1 17
  • 1.遠(yuǎn)古時(shí)代的故事那些經(jīng)歷過手工管理內(nèi)存(MRC)時(shí)代的人們凸克,一定對 iOS 開發(fā)中的內(nèi)存管理記憶猶新议蟆。那個(gè)時(shí)候大...
    MissHector閱讀 207評論 0 1
  • 內(nèi)存管理是程序在運(yùn)行時(shí)分配內(nèi)存、使用內(nèi)存萎战,并在程序完成時(shí)釋放內(nèi)存的過程咐容。在Objective-C中,也被看作是在眾...
    蹲瓜閱讀 3,001評論 1 8
  • 初高中英語教科書中大概還會(huì)介紹一部根據(jù)小說改編的同名電影<Gone with wind>蚂维, 小說的中文譯名是《飄》...
    _levi閱讀 353評論 4 2