|
我是老温,一名热爱学习的嵌入式工程师
$ ^ D9 k" v$ }4 x: l6 n0 y- K关注我,一起变得更加优秀!1.前言 最近部门不同产品接连出现内存泄漏导致的网上问题,具体表现为单板在现网运行数月以后,因为内存耗尽而导致单板复位现象。
c) L! z5 u, B一方面,内存泄漏问题属于低级错误,此类问题遗漏到现网,影响很坏;另一方面,由于内存泄漏问题很可能导致单板运行固定时间以后就复位,只能通过批量升级才能解决,实际影响也很恶劣。
1 n9 [# B9 z! ^6 S" W9 {7 v. \; O) _同时,接连出现此类问题,尤其是其中一例问题还是我们老员工修改引入,说明我们不少员工对内存泄漏问题认识还是不够深刻的。7 _( s' L; |1 d" k" j) M
本文通过介绍内存泄漏问题原理及检视方法,希望后续能够从编码检视环节就杜绝此类问题发生。6 Z& b' ]( V7 ?8 t7 R$ y
说明:预防内存泄漏问题有多种方法,如加强代码检视、工具检测和内存测试等,本文聚集于开发人员能力提升方面。) f+ w) H! {5 h3 Q9 B0 T
2.内存泄漏问题原理 2.1堆内存在C代码中的存储方式内存泄漏问题只有在使用堆内存的时候才会出现,栈内存不存在内存泄漏问题,因为栈内存会自动分配和释放。C代码中堆内存的申请函数是malloc,常见的内存申请代码如下:
/ @3 W, A/ P1 P( Y: Gchar *info = NULL; /**转换后的字符串**/
( r8 b4 Z$ j* c% d& y3 winfo = (char*)malloc(NB_MEM_SPD_INFO_MAX_SIZE);3 p4 Y8 m \1 d! [
if( NULL == info)9 g# V4 ?) R3 R: D
{( U1 y0 x: f8 J. Z
(void)tdm_error("malloc error!9 Z- t$ b! q- W H
");
`+ \, y& s2 [5 I0 i" V return NB_SA_ERR_HPI_OUT_OF_MEMORY;* V" k* I% k( F. s" k/ N, x$ b4 d% V
}) s( i- z6 O# d" c
由于malloc函数返回的实际上是一个内存地址,所以保存堆内存的变量一定是一个指针(除非代码编写极其不规范)。再重复一遍,保存堆内存的变量一定是一个指针,这对本文主旨的理解很重要。当然,这个指针可以是单指针,也可以是多重指针。
0 @5 e" i/ @) D0 a( o* tmalloc函数有很多变种或封装,如g_malloc、g_malloc0、VOS_Malloc等,这些函数最终都会调用malloc函数。
- ?8 O! N" E4 C' n2.2堆内存的获取方法看到本小节标题,可能有些同学有疑惑,上一小节中的malloc函数,不就是堆内存的获取方法吗?的确是,通过malloc函数申请是最直接的获取方法,如果只知道这种堆内存获取方法,就容易掉到坑里了。一般的来讲,堆内存有如下两种获取方法:
3 J; B8 P$ Q: _% V; i2 n, V方法一:将函数返回值直接赋给指针,一般表现形式如下:
, K8 A( N1 G4 qchar *local_pointer_xx = NULL;# T" |3 o) a% D9 j
local_pointer_xx = (char*)function_xx(para_xx, …); ?! ^; t! A: q2 b
该类涉及到内存申请的函数,返回值一般都指针类型,例如:5 V; F$ G4 A# L/ V
GSList* g_slist_append (GSList *list, gpointer data);
( f# T. ]) [% p. \' _- Z方法二:将指针地址作为函数返回参数,通过返回参数保存堆内存地址,一般表现形式如下:, i/ N6 q/ h& p, L9 I. g* i
int ret;, C( ]3 d1 s7 o8 y8 p2 q" m
char *local_pointer_xx = NULL; /**转换后的字符串**/
9 Z# n' _5 i! N- ?" wret = (char*)function_xx(..., &local_pointer_xx, ...);
) F( @+ {, B" X9 ?该类涉及到内存申请的函数,一般都有一个入参是双重指针,例如:; q) A4 [6 z5 m E; q( ` N- m
__STDIO_INLINE _IO_ssize_t;+ [8 W# F$ F; z
getline (char **__lineptr, size_t *__n, FILE *__stream);
5 v4 p3 J t6 T' `% D7 h: g前面说通过malloc申请内存,就属于方法一的一个具体表现形式。其实这两类方法的本质是一样的,都是函数内部间接申请了内存,只是传递内存的方法不一样,方法一通过返回值传递内存指针,方法二通过参数传递内存指针。
. M8 R* G x- Q0 q2.3内存泄漏三要素最常见的内存泄漏问题,包含以下三个要素:
+ j9 X! [! d$ e- L要素一:函数内有局部指针变量定义;
' J( x% D3 C& f: Q6 @& d要素二:对该局部指针有通过上一小节中“两种堆内存获取方法”之一获取内存;
: p: a/ [' W/ L; Q要素三:在函数返回前(含正常分支和异常分支)未释放该内存,也未保存到其它全局变量或返回给上一级函数。0 C: U0 [% e6 ~7 a5 L8 o
2.4内存释放误区稍微使用过C语言编写代码的人,都应该知道堆内存申请之后是需要释放的。但为何还这么容易出现内存泄漏问题呢?一方面,是开发人员经验不足、意识不到位或一时疏忽导致;另一方面,是内存释放误区导致。很多开发人员,认为要释放的内存应该局限于以下两种:
9 L+ e% Q6 \2 y' Z1) 直接使用内存申请函数申请出来的内存,如malloc、g_malloc等;4 j4 u: j5 m+ V! m, ]) ~
2)该开发人员熟悉的接口中,存在内存申请的情况,如iBMC的兄弟,都应该知道调用如下接口需要释放list指向的内存:- V `- A" r" J- c( z* p6 v
dfl_get_object_list(const char* class_name, GSList **list);
7 S- ?7 G R. R$ d按照以上思维编写代码,一旦遇到不熟悉的接口中需要释放内存的问题,就完全没有释放内存的意识,内存泄漏问题就自然产生了。) C* C' t/ r/ b
0 T5 ?( Y4 _$ l5 ^8 B) A3.内存泄漏问题检视方法 检视内存泄漏问题,关键还是要养成良好的编码检视习惯。与内存泄漏三要素对应,需* V* H# m& I5 r: K6 k6 O
要做到如下三点:
: K5 E1 B! h( I4 f1) 在函数中看到有局部指针,就要警惕内存泄漏问题,养成进一步排查的习惯2 o5 w. O( t! V# e
2) 分析对局部指针的赋值操作,是否属于前面所说的“两种堆内存获取方法”之一,如果是,就要分析函数返回的指针到底指向啥?是全局数据、静态数据还是堆内存?对于不熟悉的接口,要找到对应的接口文档或源代码分析;又或者看看代码中其它地方对该接口的引用,是否进行了内存释放;. G. N* E! X; d3 J5 f
3) 如果确认对局部指针存在内存申请操作,就需要分析该内存的去向,是会被保存在全局变量吗?又或者会被作为函数返回值吗?如果都不是,就需要排查函数所有有”return“的地方,保证内存被正确释放。
! J5 F, q' d) C5 B! }- }3 Y- q0 b) L
原文:https://my.oschina.net/u/4526289/blog/4539592& P$ N+ Q A) A( w. I
5 u( x6 f5 h" L1 f/ q- ?/ e$ ]* `" D
-END-
0 l+ a2 @8 A* z; i往期推荐:点击图片即可跳转阅读 |) R6 m9 g% ~0 W! x9 I
* A2 }- m6 @8 A) m9 g ) y# r6 T$ `% d7 u; n. a I: I
' n( J0 l0 O* O/ `7 ^# I
1 H7 j; B ]* z! P3 F 9 `4 B7 k& N; _+ [
c31kfmchx2264032303722.jpg
0 t2 u# \( k. J% ^9 L
! \: Y7 o4 J2 H/ O( X 嵌入式 C 语言,位操作的几种常见用法。
7 r, ?3 Z/ i- ]# G6 c5 g
1 Y" N7 J/ _' [, t/ B' G ) F: K9 \7 ~( Z) t+ S8 m* f
* @% |. R% Q1 U% `9 J9 W, w7 X
1 y A' [4 I" l% l
0 Z7 V7 p( U! l, A
# n/ I- G2 c- \; t7 D
- s& z/ p2 g9 y- W" q$ v0 r3 I 5 N, y5 X) i+ y
6 Y( B1 A; h- T2 C
4 a: k" `* [6 y% _3 }" q
ckwxczm1erf64032303822.jpg
+ k7 O5 }" q- ]( G3 G0 H6 | " G+ q% A5 p2 _
如何设计有缺陷的嵌入式软件?
7 u( V* D1 _( d( g! C5 B: { 2 A C7 s& M! x0 v. p5 o
9 {0 {3 q; }# f+ X8 N
4 ]# E* T) |4 o1 [' Y) |6 B2 m& q
3 z \% q, r3 q% j2 t* \$ P' {
" m" D+ h$ |" l, S- @" s; V$ H* z. U5 N2 i
8 X e% I+ L; j
+ K4 d& M0 _4 d5 C1 N0 j! H
. R% a/ J, v3 d! S% K ( s, x5 q' P7 ] `3 K2 G% e7 {
oitvbtpkle464032303922.jpg
, P% }' ]" t% @1 G0 G( J7 ~& Z
- B% l# M, {) o 在内卷中突围,开始学习嵌入式 AI 和多媒体应用技术!
& g+ t# a3 J' p我是老温,一名热爱学习的嵌入式工程师/ f0 f7 ]$ ?, C; o9 W' `
关注我,一起变得更加优秀!
: }4 p. N: L) C8 N6 C/ F: l- K
otcjzjrmnmm64032304023.png
|
|