C内存泄漏-产生原因及排查方法

C语言比较灵活简洁,但这同样使得对开发人员的要求变高,其中很常见的问题便是内存泄漏问题了。产生该问题的原因情况不多,但是我们在进行问题排查时的手段也寥寥无几,令人非常头疼,所以在此对其作出总结,方便自己回忆观看。

这里首先介绍内存泄漏的基本概念,那就是程序执行过程中,系统中的可供使用的内存越来越少,最后直至系统无内存可以使用导致系统卡死复位。

那么好,什么情况会使系统内存越来越少呢,其实很简单,那就是申请了堆内存空间却没有对其进行释放,因为程序或者函数退出对于栈空间里的内容是会自动释放的,而对堆内存却需要程序员自己去管理释放。常见的堆内存申请函数有molloc()及相关的一系列衍生动态内存申请函数,当然一般也会有对应的free()去释放堆内存空间。内存泄漏的根本原因便是如此了,开发人员对于molloc()和free()的组合使用一般也是会有意识的,但是内存泄漏为什么还是很常见呢?其实可以从以下两个方面来进行说明。

堆内存获取

方法一:molloc()函数直接申请,函数返回值将申请内存空间首地址赋给指针。

int *mem_pointer = NULL;
mem_pointer = (int *)molloc(sizeof(int));

 

方法二:指针作为函数参数传入功能函数,内部将申请内存首地址赋给该指针。

int ret;
int *mem_pointer = NULL;

int mem_func(int *src_ptr)
{
  src_ptr = (int *)molloc(sizeof(int));
  ...
  ...
  return 0;
}

ret
= mem_func(mem_pointer);

 

这两种申请方式的本质其实是一样的,但是前者可以比较直观的意识到内存释放问题,而后者若是不注意间可能就要陷到坑里了,更不容易察觉到有内存需要释放。对于第二种情况,想要避免内存泄漏问题的产生,那么就要求开发人员对使用接口的内部实现有较深了解,或者说知道有指针传入的函数使用时可能产生内存泄漏风险,提前去对代码进行检查规避。

内存泄漏要素

  1. 函数局部变量定义。
  2. 对该局部变量使用上面两种方法之一获取内存。
  3. 函数返回时在正常或异常分支未释放该内存,也未保存到其他全局变量或返回给上一级函数。

  ps:全局变量申请同样要注意释放问题,但是一般遵从代码规范的话,需要尽量减少全局变量的使用,全局变量内存未释放只有在程序多次执行又结束才会较快发现内存泄漏,这种情况较少,需要稍加注意。

内存泄漏排查

内存泄漏可供检查的方法其实也不算太少,但是一旦代码规模上来的话,还是比较麻烦,一般的话有以下三种方法:

  1. 代码检查
  2. 内存测试
  3. 工具检测

首先代码检查是最基本的,一旦是使用了mollc族的函数都要去注意内存的释放问题。那我最近自检出来的问题来举例的话,那便是molloc出了一块内存,然后却对内存首地址指针进行了自增操作,随后对该指针进行释放的时候自然也是无效的释放,产生了内存泄漏。这个问题其实准确的说是在测试时候发现的,采用的也就是所谓的内存测试。

就是周期性的去对系统的内存信息进行打印在日志中,隔段时间去看可用内存是否减少即可。这里附上一段简单脚本:

 1 #!/bin/sh
 2 #
 3 # 
 4 
 5 total_count=0
 6 while [ 1 ]
 7 do
 8     date
 9     let total_count++
10     echo "test total_count=$total_count"         
11     echo ""
12 
13     if [ 0 == $(($total_count % 500)) ] ; then
14         log "cat /proc/meminfo"
15         cat /proc/meminfo 
16     fi
17 
18     sleep 60
19     echo ""
20 done
21  

 

最后那便是工具的使用了,linux下的话推荐valgrind,使用起来可能稍微有些门槛,但对于程序员来说应该不成问题,这里附上初略一个使用教程。

https://zhuanlan.zhihu.com/p/75416381

类似的工具还有BPF,mtrace等等,但是所注重的侧重点和使用都不太一样。

posted @ 2022-01-23 17:36  阿甘横行  阅读(2702)  评论(0编辑  收藏  举报