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

    文章

    7646

    浏览量

    146141
  • 内存泄露
    +关注

    关注

    0

    文章

    7

    浏览量

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    C++ 11 14 17 20内存管理-指针、智能指针和内存池基础与提升

    大模型在云端吞吐着海量数据,当自动驾驶汽车在毫秒间做出生死决策,我们不得不重新审视支撑这一切数字大厦的基石——C++内存管理。尽管高级语言层出不穷,但在对性能、延迟和资源控制有着极致要求的底层核心领域,
    的头像 发表于 04-20 15:51 518次阅读

    keil中c语言的动态分配内存

    C程序中,通常将内存划分为以下六个区域: (1)内核区域。这块区域是操作系统的,用户不能使用。 (2)栈区。主要用于存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。栈内存分配运算内置
    发表于 01-21 06:04

    芯片买卖是人民币玩家的游戏普通人慎入,赌错后果很严重。元器件

    元器件
    芯广场
    发布于 :2026年01月16日 16:03:42

    C语言嵌入式系统编程注意事项-内存操作

    C语言嵌入式系统编程注意事项之内存操作 在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/
    发表于 01-04 07:31

    C语言内存池使用

    C语言内存管理,从来都是一个让人头秃的问题。要想更自由地管理内存,就必须去堆中申请,然后还需要考虑何时释放,万一释放不当,或者没有及时释放,造成的后果都是难以估量的。 当然如果就这
    发表于 12-11 07:57

    C语言的编程技巧

    一个成员是一个未知大小的数组,适用于动态分配内存并关联一个可变长度的数组。‌ ‌3、匿名结构体和联合体‌:C语言允许在结构体或联合体中定义不带标签的内部结构体或联合体,简化代码结构。 ‌4
    发表于 11-27 06:46

    C语言特性

    访问内存地址,对内存中的数据进行读写操作,还可以控制硬件的寄存器,实现对硬件设备的精确控制。这种底层操作的能力,使得 C 语言在嵌入式开发中能够充分发挥硬件的优势,实现各种复杂的功能。
    发表于 11-24 07:01

    C语言和单片机C语言有什么差异

    单片机c语言相对于普通C语言增加了一些基本的指令,还有变量的赋值是16进制,当然单片机c语言只牵
    发表于 11-14 07:55

    WebGL/Canvas 内存泄露分析

    在构建高性能、长周期运行的 WebGL/Canvas 应用(如 3D 编辑器、数据可视化平台)时,内存管理是一个至关重要且极具挑战性的课题。 开发者通常面临的内存泄漏问题,其根源远比简单
    的头像 发表于 10-21 11:40 537次阅读
    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 813次阅读
    灵活高效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 736次阅读
    灵活高效双引擎驱动: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 1204次阅读
    主流的 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语言C语言循环控制

    C语言编程中,循环结构是至关重要的,它可以让程序重复执行特定的代码块,从而提高编程效率。然而,为了避免程序进入无限循环,C语言提供了多种循环控制语句,如break、continue和
    的头像 发表于 04-29 18:49 2221次阅读
    深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:<b class='flag-5'>C</b><b class='flag-5'>语言</b>循环控制