可能不少開發(fā)者都遇到過內存泄漏導致的網(wǎng)上問題,具體表現(xiàn)為單板在現(xiàn)網(wǎng)運行數(shù)月以后,因為內存耗盡而導致單板復位現(xiàn)象。一方面,內存泄漏問題屬于比較淺顯的錯誤,此類問題遺漏到現(xiàn)網(wǎng),影響不好;另一方面,由于內存泄漏問題很可能導致單板運行固定時間以后就復位,只能通過批量升級才能解決,實際影響不佳。本文通過介紹內存泄漏問題原理及檢視方法,希望后續(xù)能夠從編碼檢視環(huán)節(jié)就杜絕此類問題發(fā)生。
說明:預防內存泄漏問題有多種方法,如加強代碼檢視、工具檢測和內存測試等,本文聚集于開發(fā)人員能力提升方面。
堆內存在C代碼中的存儲方式
內存泄漏問題只有在使用堆內存的時候才會出現(xiàn),棧內存不存在內存泄漏問題,因為棧內存會自動分配和釋放。C代碼中堆內存的申請函數(shù)是malloc,常見的內存申請代碼如下:
char *info = NULL;
info = (char*)malloc(NB_MEM_SPD_INFO_MAX_SIZE);
if( NULL == info)
{
(void)tdm_error("malloc error!\n");
return NB_SA_ERR_HPI_OUT_OF_MEMORY;
}
由于malloc函數(shù)返回的實際上是一個內存地址,所以保存堆內存的變量一定是一個指針(除非代碼編寫極其不規(guī)范)。再重復一遍,保存堆內存的變量一定是一個指針,這對本文主旨的理解很重要。當然,這個指針可以是單指針,也可以是多重指針。
malloc函數(shù)有很多變種或封裝,如g_malloc、g_malloc0、VOS_Malloc等,這些函數(shù)最終都會調用malloc函數(shù)。
堆內存的獲取方法
看到本小節(jié)標題,可能有些同學有疑惑,上一小節(jié)中的malloc函數(shù),不就是堆內存的獲取方法嗎?的確是,通過malloc函數(shù)申請是最直接的獲取方法,如果只知道這種堆內存獲取方法,就容易掉到坑里了。一般的來講,堆內存有如下兩種獲取方法:
方法一:將函數(shù)返回值直接賦給指針,一般表現(xiàn)形式如下:
char *local_pointer_xx = NULL;
local_pointer_xx = (char*)function_xx(para_xx, …);
該類涉及到內存申請的函數(shù),返回值一般都指針類型,例如:
GSList* g_slist_append (GSList *list, gpointer data)
方法二:將指針地址作為函數(shù)返回參數(shù),通過返回參數(shù)保存堆內存地址,一般表現(xiàn)形式如下:
int ret;
char *local_pointer_xx = NULL;
ret = (char*)function_xx(..., &local_pointer_xx, ...);
該類涉及到內存申請的函數(shù),一般都有一個入?yún)⑹请p重指針,例如:
__STDIO_INLINE _IO_ssize_t
getline (char **__lineptr, size_t *__n, FILE *__stream)
前面說通過malloc申請內存,就屬于方法一的一個具體表現(xiàn)形式。其實這兩類方法的本質是一樣的,都是函數(shù)內部間接申請了內存,只是傳遞內存的方法不一樣,方法一通過返回值傳遞內存指針,方法二通過參數(shù)傳遞內存指針。
內存泄漏三要素
內存釋放誤區(qū)
稍微使用過C語言編寫代碼的人,都應該知道堆內存申請之后是需要釋放的。但為何還這么容易出現(xiàn)內存泄漏問題呢?一方面,是開發(fā)人員經(jīng)驗不足、意識不到位或一時疏忽導致;另一方面,是內存釋放誤區(qū)導致。很多開發(fā)人員,認為要釋放的內存應該局限于以下兩種:
(1)?直接使用內存申請函數(shù)申請出來的內存,如malloc、g_malloc等;
(2)?該開發(fā)人員熟悉的接口中,存在內存申請的情況,如iBMC的兄弟,都應該知道調用如下接口需要釋放list指向的內存:
dfl_get_object_list(const char* class_name, GSList **list)
按照以上思維編寫代碼,一旦遇到不熟悉的接口中需要釋放內存的問題,就完全沒有釋放內存的意識,內存泄漏問題就自然產(chǎn)生了。
檢視內存泄漏問題,關鍵還是要養(yǎng)成良好的編碼檢視習慣。與內存泄漏三要素對應,需
要做到如下三點:
(1)?在函數(shù)中看到有局部指針,就要警惕內存泄漏問題,養(yǎng)成進一步排查的習慣;
(2)?分析對局部指針的賦值操作,是否屬于前面所說的“兩種堆內存獲取方法”之一,如果是,就要分析函數(shù)返回的指針到底指向啥?是全局數(shù)據(jù)、靜態(tài)數(shù)據(jù)還是堆內存?對于不熟悉的接口,要找到對應的接口文檔或源代碼分析;又或者看看代碼中其它地方對該接口的引用,是否進行了內存釋放;
(3)?如果確認對局部指針存在內存申請操作,就需要分析該內存的去向,是會被保存在全局變量嗎?又或者會被作為函數(shù)返回值嗎?如果都不是,就需要排查函數(shù)所有有”return“的地方,保證內存被正確釋放。
-END-
來源 |?華為云社區(qū)
作者 | 技術火炬手
|?整理文章為傳播相關技術,版權歸原作者所有?|
|?如有侵權,請聯(lián)系刪除?|
【1】C語言進階技術:同事這些操作把我驚呆了!
【2】初學者:如何學好C語言?
【3】推薦給初學者的箴言:如何學好C語言
【4】C語言與C++40 年的愛恨情仇!
【5】C語言中的短路現(xiàn)象
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!