摘要:通过介绍内存透露问题原理及检视办法,心愿后续可能从编码检视环节就杜绝内存透露导致的网上问题产生。
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“的中央,保障内存被正确开释。
点击关注,第一工夫理解华为云陈腐技术~