如何理解Memory leak

大雄45發表於2022-11-02
導讀 有很多情況會導致這一問題,像重複使用的某個結構體/物件,當再次複用時沒有清理上一次使用遺留的資料、系統中存在cache,但cache的過期策略設定不得當等等。

如何理解Memory leak如何理解Memory leak

大家好,我是小風哥,今天和大家聊一聊記憶體洩漏這個話題。

我之前寫過好幾篇關於記憶體的文章,在這些文章講到記憶體申請時我很喜歡用停車場來做類比,記憶體申請就好比去停車場找停車位,找到停車位後你就可以把車停在這裡。

從這個類比看什麼是記憶體洩漏呢?記憶體洩漏看上去是停車場的車輛只進不出導致最終找不到停車位,從程式設計師的角度看就是記憶體只申請取不釋放,如果你去問,可能有不少人認為記憶體洩漏就是這麼回事。

然而這其實是不全面的。

申請過多記憶體

首先記憶體只申請不釋放未必就是記憶體洩漏,有可能是你的程式的確需要申請很多記憶體,這是正常的,然而如果是bug導致申請了很多記憶體,這就是記憶體洩漏了,或者也有人將其稱為space leak,意思是申請的記憶體超過了正常所需;不管是有意無意,總之在這種情況下你依然保持對這些記憶體的引用,因此你總可以找到這些記憶體並刪除它們,就看你刪不刪。

有很多情況會導致這一問題,像重複使用的某個結構體/物件,當再次複用時沒有清理上一次使用遺留的資料、系統中存在cache,但cache的過期策略設定不得當等等。

記憶體無法刪除

另一類比較有趣的記憶體洩漏是說你申請了一些記憶體,但最終卻沒有什麼指向它們:

void memory_leak() { 
  char* mem = (char*)malloc(1024); 
  // just return 
}

在這段程式碼中我們申請了1k記憶體,然而當memory_leak函式返回後你就再也不知道這段記憶體到底在哪裡了!

用停車場的示例來說就是有些司機太過土豪,家裡的車太多以至於把將車放在停車場這件事忘掉了,導致這些車根本就不會有人再開走,因此白白浪費停車位,並導致可用車位越來越少,而對於程式設計來說就是粗心大意的程式設計師申請了一些記憶體後最終“忘掉”了,再也不會有什麼東西(變數/指標)指向這些記憶體,因此在這種情況下你沒有辦法再找到這些記憶體並將其刪除。

記憶體碎片
這也算的上是一類特殊的記憶體洩漏,用停車場的例子來說就是兩個停車位中間停靠了一輛小型老年代步車,導致儘管這兩個停車位剩餘的空間足夠大但又恰好都沒有辦法再停靠一輛小汽車。

假定我們系統中寶貴的記憶體大小隻有8位元組,其中有兩個位元組已經分配出去了,就像這樣:

如何理解Memory leak如何理解Memory leak

現在,系統中空閒的記憶體是6位元組,下一次的記憶體申請需要分配5位元組,糟糕,我們已經沒有辦法再找到連續的5個位元組大小的記憶體空間了,儘管全部空間的記憶體還有6位元組,這就是所謂的記憶體碎片問題。

而對於記憶體分配器來說如果出現這種情況那麼將不得不借助作業系統的幫助來擴大堆區,因此看起來我們的程式佔據的記憶體越來越多,儘管實際上程式可能並不需要那麼多記憶體,僅僅是因為記憶體碎片的原因導致一部分記憶體無法被再次被利用起來。

然而對於現代作業系統尤其具備虛擬記憶體能力的系統來說,記憶體碎片問題通常可能並不會和我們想象的那樣嚴重,原因就在於分配的記憶體只需要在虛擬地址空間上連續而不必在實體記憶體上也連續,假定我們在虛擬記憶體地址空間需要存放“aabbccdd”這樣的字串,在虛擬地址空間上看這是連續的就像這樣:

如何理解Memory leak如何理解Memory leak

但在實體記憶體上可能是這樣存放的:

如何理解Memory leak如何理解Memory leak

可以看到,利用虛擬記憶體我們可以更加充分靈活的利用“邊邊角角”的實體記憶體,從而減少記憶體碎片帶來的影響。

關於虛擬記憶體更詳細的講解你可以參考《深入理解作業系統》虛擬記憶體一章,關於公眾號“碼農的荒島求生”並回復“作業系統”即可。

如果你的程式需要重複申請很多物件/資料/結構體,並在最後一次性全部釋放,那麼記憶體池是一個避免記憶體碎片不錯的選擇,原理在於儘管從記憶體池的角度看會有碎片,但當我們以記憶體池大小為單位從堆區中申請釋放記憶體時,這種碎片將不復存在。

關於記憶體池你可以參考這篇《高效能伺服器記憶體池是如何實現的》。

記憶體洩漏帶來的問題

在現代作業系統中除非你的程式執行時間足夠長或者申請的記憶體足夠快足夠多否則記憶體洩漏可能並不是什麼大問題,你甚至可能都察覺不出來有記憶體洩漏,因為當程式執行結束後其佔據的記憶體會被作業系統收回,在這種情況下你可能不必過於關心這個問題,但對於長時間執行的伺服器端程式、資料庫程式、作業系統等,記憶體洩漏就屬於比較嚴重的問題了,因為這些程式必須時刻線上,任何微小的記憶體洩漏在時間的加持下都會非常明顯。

記憶體持續洩漏會發生什麼?

如果記憶體持續洩漏那麼你的電腦可能會爆炸。。。這。。。當然是不可能的。

你的系統會慢到炸是有可能的。

記憶體的申請速度會對系統效能產生很大的影響,當系統記憶體不足時,記憶體分配器找到一塊滿足要求的空閒記憶體塊將更加困難耗時更多,當程式消耗的記憶體超過實體記憶體大小時虛擬記憶體系統(如果有的話)開始發揮作用,將程式地址空間中不常用的一部分swap出去,此時系統效能將快速下降,表現出來的就是程式設計師執行變慢、卡頓。

當然,根據系統配置,像 ,可能會將消耗記憶體很多的程式kill掉,這就是Out of Memory killer,簡稱oom killer。

記憶體洩漏檢測工具

不像程式崩潰Core dump,這類問題透過debug通常能獲取一些線索,但記憶體洩漏問題就沒那麼直接了,尤其對於C/C++程式來說,這時我們將不得不借助必要的工具。

那麼我們該利用什麼的工具來解決記憶體洩漏問題呢?下一篇文章將給你答案。

這個公眾號裡所有的文章都已經彙總在了Github上,地址 ,你也可以點選左下方“閱讀原文”直達,歡迎訪問,求star,哈哈。

原文來自:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2852449/,如需轉載,請註明出處,否則將追究法律責任。

相關文章