电子产业一站式赋能平台

PCB联盟网

搜索
查看: 78|回复: 0
收起左侧

当嵌入式软件发生内存泄漏时,有哪些检视方法?

[复制链接]

561

主题

561

帖子

3987

积分

四级会员

Rank: 4

积分
3987
发表于 2025-3-16 12:00:00 | 显示全部楼层 |阅读模式
我是老温,一名热爱学习的嵌入式工程师
7 C* D2 x1 a" w( q& [! r7 F关注我,一起变得更加优秀!1.内存泄漏问题原理 1.1堆内存在C代码中的存储方式内存泄漏问题只有在使用堆内存的时候才会出现,栈内存不存在内存泄漏问题,因为栈内存会自动分配和释放。C代码中堆内存的申请函数是malloc,常见的内存申请代码如下:: _/ y/ G3 _! L/ P# n( D
char *info = NULL;    /**转换后的字符串**/
! g9 I( |9 N1 c3 b. `info = (char*)malloc(NB_MEM_SPD_INFO_MAX_SIZE);
: H; ~8 |" g% K0 Z: @if( NULL == info)( T1 L5 q6 d, y" t9 x* c' D# ]3 N
{7 T3 s7 T/ S4 b- K
    (void)tdm_error("malloc error!! c, `* M; x9 w' @, F. H
");
( \* V0 c" l8 ~$ N% Z    return NB_SA_ERR_HPI_OUT_OF_MEMORY;
. A: x/ K# ?9 \# X( E1 r}
$ I" V" b5 K! C  O5 Z" V由于malloc函数返回的实际上是一个内存地址,所以保存堆内存的变量一定是一个指针(除非代码编写极其不规范)。再重复一遍,保存堆内存的变量一定是一个指针,这对本文主旨的理解很重要。当然,这个指针可以是单指针,也可以是多重指针。2 K* X) S5 c0 p* z0 k
malloc函数有很多变种或封装,如g_malloc、g_malloc0、VOS_Malloc等,这些函数最终都会调用malloc函数。' j; a- i7 a! g& u, x
1.2堆内存的获取方法看到本小节标题,可能有些同学有疑惑,上一小节中的malloc函数,不就是堆内存的获取方法吗?的确是,通过malloc函数申请是最直接的获取方法,如果只知道这种堆内存获取方法,就容易掉到坑里了。一般的来讲,堆内存有如下两种获取方法:7 u/ d) s  `: i. Z4 H& k
方法一:将函数返回值直接赋给指针,一般表现形式如下:
& E) H; [* _5 i+ l% }  Y5 {char *local_pointer_xx = NULL;# |4 X6 @/ D1 [7 R
local_pointer_xx = (char*)function_xx(para_xx, …);
3 u) O- U% a5 l8 a% N该类涉及到内存申请的函数,返回值一般都指针类型,例如:
1 B3 P3 Z* y' I! f- BGSList* g_slist_append (GSList   *list, gpointer  data);
$ H3 a+ x7 B0 |9 T方法二:将指针地址作为函数返回参数,通过返回参数保存堆内存地址,一般表现形式如下:
5 {( t& r2 \% o1 }! S" d8 ^3 wint ret;- ^8 E# [+ ?: ~6 T9 E5 ^
char *local_pointer_xx = NULL;    /**转换后的字符串**/
5 m. a8 F' r! h4 k& T% hret = (char*)function_xx(..., &local_pointer_xx, ...);
' K$ {% J6 s; A& g该类涉及到内存申请的函数,一般都有一个入参是双重指针,例如:8 m/ |# e' X2 b" l* a) i
__STDIO_INLINE _IO_ssize_t;
% }8 x( s% i0 G. y( \$ J4 F+ agetline (char **__lineptr, size_t *__n, FILE *__stream);
0 \0 o! }* @3 i; {* l! F2 r" Z前面说通过malloc申请内存,就属于方法一的一个具体表现形式。其实这两类方法的本质是一样的,都是函数内部间接申请了内存,只是传递内存的方法不一样,方法一通过返回值传递内存指针,方法二通过参数传递内存指针。8 z8 D7 R) c. k/ n
1.3内存泄漏三要素最常见的内存泄漏问题,包含以下三个要素:
2 K* h2 W( @7 ?) _4 D% j要素一:函数内有局部指针变量定义;& X3 q, G3 e+ O" E
要素二:对该局部指针有通过上一小节中“两种堆内存获取方法”之一获取内存;
! P# y8 T. e3 p1 z要素三:在函数返回前(含正常分支和异常分支)未释放该内存,也未保存到其它全局变量或返回给上一级函数。# K4 x* T* G5 x& m/ ^
1.4内存释放误区稍微使用过C语言编写代码的人,都应该知道堆内存申请之后是需要释放的。但为何还这么容易出现内存泄漏问题呢?一方面,是开发人员经验不足、意识不到位或一时疏忽导致;另一方面,是内存释放误区导致。很多开发人员,认为要释放的内存应该局限于以下两种:; G: o8 r! n1 r: F
1) 直接使用内存申请函数申请出来的内存,如malloc、g_malloc等;
* B# U9 L  U9 w% k& @2)该开发人员熟悉的接口中,存在内存申请的情况,如iBMC的兄弟,都应该知道调用如下接口需要释放list指向的内存:
5 Z$ j4 j+ Z6 R$ u. Adfl_get_object_list(const char* class_name, GSList **list);
+ C7 j' c# J3 u. O) [) O5 O. A按照以上思维编写代码,一旦遇到不熟悉的接口中需要释放内存的问题,就完全没有释放内存的意识,内存泄漏问题就自然产生了。3 J% \+ ~4 [0 W1 z
2.内存泄漏问题检视方法检视内存泄漏问题,关键还是要养成良好的编码检视习惯。与内存泄漏三要素对应,需
" p$ [+ J! R( S% R/ a+ a3 R要做到如下三点:
4 z/ Z3 c( r9 H1 i/ W1) 在函数中看到有局部指针,就要警惕内存泄漏问题,养成进一步排查的习惯
& ]( u: N. L+ J% R- _2) 分析对局部指针的赋值操作,是否属于前面所说的“两种堆内存获取方法”之一,如果是,就要分析函数返回的指针到底指向啥?是全局数据、静态数据还是堆内存?对于不熟悉的接口,要找到对应的接口文档或源代码分析;又或者看看代码中其它地方对该接口的引用,是否进行了内存释放;! A7 F5 h2 D! R4 @
3) 如果确认对局部指针存在内存申请操作,就需要分析该内存的去向,是会被保存在全局变量吗?又或者会被作为函数返回值吗?如果都不是,就需要排查函数所有有”return“的地方,保证内存被正确释放。
6 W& F' o7 U! ?, D% Z5 K来源:https://my.oschina.net/u/4526289/blog/4539592-END-
) _, L  B4 ^# d: l往期推荐:点击图片即可跳转阅读9 h: {! [! n: ^! ]* j4 j$ u

aq4a2i5h0xi640120512.jpg

aq4a2i5h0xi640120512.jpg
. M/ \) g  x! F5 z# u0 K
非常不错的单片机科普总结,值得收藏!, q& _4 I7 Z/ [9 D# V

4z1wffrgs13640120613.jpg

4z1wffrgs13640120613.jpg

3 r! g  G4 Q5 S" [& f  h0 g明显感觉到一种趋势,嵌入式端侧AI的学习成本,在不断下降!
# Z( ], C( P( \, h7 k

s2wu3kbpcwg640120713.jpg

s2wu3kbpcwg640120713.jpg

$ t/ x- j7 C# p8 t( |6 R嵌入式设备采用经过认证的软件协议栈,开发过程可以事半功倍!" ^1 p$ v' h" t
我是老温,一名热爱学习的嵌入式工程师. X* a% \$ g( I$ {5 q2 p! d7 e( }
关注我,一起变得更加优秀!
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表