乐趣区

关于c:C语言内存泄露很严重如何应对

摘要: 通过介绍内存透露问题原理及检视办法,心愿后续可能从编码检视环节就杜绝内存透露导致的网上问题产生。

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“的中央,保障内存被正确开释。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版