0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

电子设计 来源:电子设计 作者:电子设计 2020-10-30 09:58 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

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

内存泄漏是比较难查的 bug 之一?有什么查找技巧吗?欢迎留言交流~

审核编辑 黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    183

    文章

    7642

    浏览量

    144571
  • 内存泄露
    +关注

    关注

    0

    文章

    7

    浏览量

    2134
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    WebGL/Canvas 内存泄露分析

    在构建高性能、长周期运行的 WebGL/Canvas 应用(如 3D 编辑器、数据可视化平台)时,内存管理是一个至关重要且极具挑战性的课题。 开发者通常面临的内存泄漏问题,其根源远比简单
    的头像 发表于 10-21 11:40 162次阅读
    WebGL/Canvas <b class='flag-5'>内存</b><b class='flag-5'>泄露</b>分析

    STM32+DP83848跑10M可以,100M丢包很严重,请问有人遇到过吗?

    大佬们,stm32驱动dp83848,跑10M可以,100M丢包很严重,请问有人遇到过吗
    发表于 08-20 10:04

    灵活高效ZBUFF — C内存数据操作库:优化内存管理的利器

    C语言开发中,高效的内存管理是提升程序性能的关键。ZBUFF作为一款灵活高效的内存数据操作库,通过优化内存分配与释放机制,为开发者提供了更
    的头像 发表于 08-14 18:01 517次阅读
    灵活高效ZBUFF — <b class='flag-5'>C</b><b class='flag-5'>内存</b>数据操作库:优化<b class='flag-5'>内存</b>管理的利器

    灵活高效双引擎驱动:ZBUFF让C语言内存操作更智能!

    高性能系统。 一、ZBUFF核心库常用函数 如果小伙伴们此前没有接触过C语言或者不精通C语言,把ZBUFF核心库当成新库来学习即可,建议先从熟悉相关函数的功能及其参数意义入手。 感兴趣
    的头像 发表于 08-11 13:27 433次阅读
    灵活高效双引擎驱动:ZBUFF让<b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>内存</b>操作更智能!

    主流的 MCU 开发语言为什么是 C 而不是 C++?

    在单片机的地界儿里,C语言稳坐中军帐,C++想分杯羹?难喽。咱电子工程师天天跟那针尖大的内存空间较劲,C++那些花里胡哨的玩意儿,在这儿真玩
    的头像 发表于 05-21 10:33 777次阅读
    主流的 MCU 开发<b class='flag-5'>语言</b>为什么是 <b class='flag-5'>C</b> 而不是 <b class='flag-5'>C</b>++?

    如何让SDK slfifosync支持FPGA 100MHz?

    CY_FX_SLFIFO_GPIF_16_32BIT_CONF_SELECT 改成0(16-BIT data bus). 但是从PC上读到的数据掉失很严重, 我想SDK slfifosync支持FPGA 100MHz?
    发表于 05-06 08:11

    快速搞懂C语言程序内存分区!

    在程序运行过程中,操作系统会根据程序的需要,将内存划分为多个功能不同的区段,以便更高效地管理内存资源和确保程序的稳定运行。不同的内存区段负责存储不同类型的数据和代码,涵盖了从程序指令、全局变量
    的头像 发表于 03-14 17:37 1329次阅读
    快速搞懂<b class='flag-5'>C</b><b class='flag-5'>语言</b>程序<b class='flag-5'>内存</b>分区!

    mpu6050航向角漂移很严重的原因及解决方法

    mpu6050航向角漂移很严重的原因及解决方案
    的头像 发表于 03-01 11:47 4177次阅读

    使用ADS1298采集的心电信号已经可以显示出来,只要测试时呼吸咳嗽时心电的基线漂移会很严重,如何解决?

    使用ADS1298采集的心电信号已经可以显示出来,但是只要测试时呼吸咳嗽时 心电的基线漂移会很严重,另外会有50hz的工频干扰请问如何解决这些问题
    发表于 02-13 06:33

    ADS1298在采集心电信号时出现了很严重的工频干扰,每个通道都有,而且相对于心电信号来说非常大,怎么解决?

    在采集心电信号时出现了很严重的工频干扰,每个通道都有,而且相对于心电信号来说非常大,心电信号几乎埋没了。求如何解决这个问题
    发表于 02-12 08:37

    ADS1298 DVDD的电流为什么那么大?

    我之前发过一个帖子说只有2路波形,其他6路波形干扰很严重,后来用50HZ软件滤波才能清晰看到其他6路波形,但是总电流很大,我测了一下AVDD那一路电流挺小的,5mA以内,DVDD那一路28mA,还望TI员工给看看,谢谢~~!
    发表于 02-07 06:32

    ADS1292采集到的信号的波形,50Hz的干扰很严重怎么解决?

    ADS1292采集到的信号的波形如下: 但是50Hz的干扰很严重,已经加了右腿驱动了,还能看到明显的干扰。 请问如何把这个干扰滤除? 是需要采集之后做数字滤波吗?有没有数字滤波的算法供参考一下?
    发表于 02-05 08:05

    请问ADS1256通道上是否可接放大器?

    我使用ADS1256做模数转换器基于参考设计设计了8路信号采集,但是上电后前面放大电路的放大芯片发热很严重,我想请问ADS1256通道上是否可接放大器,(我对ADS1256的初始化让他不输出电流)
    发表于 01-16 07:06

    EE-62:在C语言中访问短字内存

    电子发烧友网站提供《EE-62:在C语言中访问短字内存.pdf》资料免费下载
    发表于 01-07 14:02 0次下载
    EE-62:在<b class='flag-5'>C</b><b class='flag-5'>语言</b>中访问短字<b class='flag-5'>内存</b>

    【高手问答】电路的功能是为了0.6v到40v之间调压,运放发热严重

    电路的功能是为了0.6v到40v之间调压。芯片的工作电压为38v,工作过程中运放发热很严重。型号:TPA1882-SR。求大佬答疑
    发表于 12-10 10:56