網站首頁 編程語言 正文
1.前言
最近部門不同產品接連出現內存泄漏導致的網上問題,具體表現為單板在現網運行數月以后,因為內存耗盡而導致單板復位現象。
一方面,內存泄漏問題屬于低級錯誤,此類問題遺漏到現網,影響很壞;另一方面,由于內存泄漏問題很可能導致單板運行固定時間以后就復位,只能通過批量升級才能解決,實際影響也很惡劣。
同時,接連出現此類問題,尤其是其中一例問題還是我們老員工修改引入,說明我們不少員工對內存泄漏問題認識還是不夠深刻的。
本文通過介紹內存泄漏問題原理及檢視方法,希望后續能夠從編碼檢視環節就杜絕此類問題發生。
說明:預防內存泄漏問題有多種方法,比如加強代碼檢視、工具檢測和內存測試等,本文聚集于開發人員能力提升方面。
2.內存泄漏問題原理
2.1堆內存在C代碼中的存儲方式
內存泄漏問題只有在使用堆內存的時候才會出現,棧內存不存在內存泄漏問題,因為棧內存會自動分配和釋放。
C代碼中堆內存的申請函數是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函數返回的實際上是一個內存地址,所以保存堆內存的變量一定是一個指針(除非代碼編寫極其不規范)。
再重復一遍,保存堆內存的變量一定是一個指針,這對本文主旨的理解很重要。當然,這個指針可以是單指針,也可以是多重指針。
malloc函數有很多變種或封裝,如g_malloc、g_malloc0、VOS_Malloc等,這些函數最終都會調用malloc函數。
2.2堆內存的獲取方法
看到本小節標題,可能有些同學有疑惑,上一小節中的malloc函數,不就是堆內存的獲取方法嗎?
的確是,通過malloc函數申請是最直接的獲取方法,如果只知道這種堆內存獲取方法,就容易掉到坑里了。
一般的來講,堆內存有如下兩種獲取方法:
方法一:將函數返回值直接賦給指針,一般表現形式如下:
char *local_pointer_xx = NULL;
local_pointer_xx = (char*)function_xx(para_xx, …);
該類涉及到內存申請的函數,返回值一般都指針類型,例如:
GSList* g_slist_append (GSList *list, gpointer data);
方法二:將指針地址作為函數返回參數,通過返回參數保存堆內存地址,一般表現形式如下:
int ret;
char *local_pointer_xx = NULL; /**轉換后的字符串**/
ret = (char*)function_xx(..., &local_pointer_xx, ...);
該類涉及到內存申請的函數,一般都有一個入參是雙重指針,例如:
__STDIO_INLINE _IO_ssize_t;
getline (char **__lineptr, size_t *__n, FILE *__stream);
前面說通過malloc申請內存,就屬于方法一的一個具體表現形式。其實這兩類方法的本質是一樣的,都是函數內部間接申請了內存,只是傳遞內存的方法不一樣,方法一通過返回值傳遞內存指針,方法二通過參數傳遞內存指針。
2.3內存泄漏三要素
最常見的內存泄漏問題,包含以下三個要素:
- 函數內有局部指針變量定義;
- 對該局部指針有通過上一小節中“兩種堆內存獲取方法”之一獲取內存;
- 在函數返回前(含正常分支和異常分支)未釋放該內存,也未保存到其它全局變量或返回給上一級函數。
2.4內存釋放誤區
稍微使用過C語言編寫代碼的人,都應該知道堆內存申請之后是需要釋放的。但為何還這么容易出現內存泄漏問題呢?
一方面,是開發人員經驗不足、意識不到位或一時疏忽導致;另一方面,是內存釋放誤區導致。
很多開發人員,認為要釋放的內存應該局限于以下兩種:
- 1)直接使用內存申請函數申請出來的內存,比如malloc、g_malloc等;
- 2)該開發人員熟悉的接口中,存在內存申請的情況,如iBMC的兄弟,都應該知道調用如下接口需要釋放list指向的內存:
dfl_get_object_list(const char* class_name, GSList **list);
按照以上思維編寫代碼,一旦遇到不熟悉的接口中需要釋放內存的問題,就完全沒有釋放內存的意識,內存泄漏問題就自然產生了。
3.內存泄漏問題檢視方法
檢視內存泄漏問題,關鍵還是要養成良好的編碼檢視習慣。與內存泄漏三要素對應,需
要做到如下三點:
- 1) 在函數中看到有局部指針,就要警惕內存泄漏問題,養成進一步排查的習慣
- 2) 分析對局部指針的賦值操作,是否屬于前面所說的“兩種堆內存獲取方法”之一,如果是,就要分析函數返回的指針到底指向啥?是全局數據、靜態數據還是堆內存?對于不熟悉的接口,要找到對應的接口文檔或源代碼分析;又或者看看代碼中其它地方對該接口的引用,是否進行了內存釋放;
- 3) 如果確認對局部指針存在內存申請操作,就需要分析該內存的去向,是會被保存在全局變量嗎?又或者會被作為函數返回值嗎?如果都不是,就需要排查函數所有有”return“的地方,保證內存被正確釋放。
原文鏈接:https://juejin.cn/post/7091480084061618206
相關推薦
- 2022-07-30 Hadoop中hdfs與yarn命令大全
- 2024-02-29 UNI-APP在自定義組件中內嵌H5/Html網頁,可自定義webview大小,加載不閃屏
- 2022-04-22 Jmeter之控制線程執行到某個結果時退出執行(第二種解決方案)
- 2022-11-17 C語言數據結構不掛科指南之棧&隊列&數組詳解_C 語言
- 2022-07-16 Mybatis-plus設置某個字段值為null
- 2022-08-29 Python如何利用pandas讀取csv數據并繪圖_python
- 2023-07-25 使用Redis做Mybatis的二級緩存
- 2023-12-19 Cannot resolve symbol異常產生原因及解決方案
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支