C语言内存泄露很严重,如何应对?

摘要:经过介绍内存泄漏问题原理及检视方法,但愿后续可以从编码检视环节就杜绝内存泄漏致使的网上问题发生。

1. 前言

最近部门不一样产品接连出现内存泄漏致使的网上问题,具体表现为单板在现网运行数月之后,由于内存耗尽而致使单板复位现象。一方面,内存泄漏问题属于低级错误,此类问题遗漏到现网,影响很坏;另外一方面,因为内存泄漏问题极可能致使单板运行固定时间之后就复位,只能经过批量升级才能解决,实际影响也很恶劣。同时,接连出现此类问题,尤为是其中一例问题仍是咱们老员工修改引入,说明咱们很多员工对内存泄漏问题认识仍是不够深入的。本文经过介绍内存泄漏问题原理及检视方法,但愿后续可以从编码检视环节就杜绝此类问题发生。segmentfault

说明:预防内存泄漏问题有多种方法,如增强代码检视、工具检测和内存测试等,本文汇集于开发人员能力提高方面。app

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, …);

该类涉及到内存申请的函数,返回值通常都指针类型,例如:code

GSList* g_slist_append (GSList   *list, gpointer  data)

方法二:将指针地址做为函数返回参数,经过返回参数保存堆内存地址,通常表现形式以下:blog

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“的地方,保证内存被正确释放。

点击关注,第一时间了解华为云新鲜技术~