【代码质量】如何使用Valgrind检测内存泄漏



1 前言

  C/C++相比其他高级编程语言,具有指针的概念,指针即是内存地址。C/C++可以通过指针来直接访问内存空间,效率上的提升是不言而喻的,是其他高级编程语言不可比拟的;比如访问内存一段数据,通过指针可以直接从内存空间读取数据,避免了中间过程函数压栈、数据拷贝甚至消息传输等待。指针是C/C++的优势,但也是一个隐患,指针是一把双刃剑,由于内存交给了程序员管理,这是存在隐患的;人嘛总会有疏忽的时候,如果申请了内存,一个疏忽忘记释放了,未释放的内存将不能被系统再申请使用,即是一直占用又不能使用,俗称内存泄露;久而久之,系统长时间运行后将申请不到内存,系统上所有任务执行失败,只能重启系统。内存交给了程序员管理,除了内存泄露的情况外,还可能引起其他的问题,总结起来包括如下几点。

  • 访问没有申请内存的空指针(空指针)
  • 访问已释放内存的指针(野指针)
  • 内存越界访问
  • 内存泄露,申请了内存没有释放
  • 重复释放内存

  虽然动态内存(这里默认指的是堆上的动态内存,由程序员管理;栈上的动态内存由系统管理)存在一定的隐患,可靠性取决于程序员的认真和细心,但因为其具有优良的灵活性和高效率以及内存复用,在实际应用中,对于一些动态数据交互场合,动态内存往往是首选。因此,一方面除了程序员本身的谨慎使用,另一方面也涌现出各类“内存异常“的检测工具,毕竟当代码量数以万计时,依靠人力去检查是不切实际的。


2 常用内存检测工具

  虽然动态内存存在隐患,但有时候又不得不使用,内存泄露也就成为一个必须杜绝的敏感问题;有需求就会有相应的解决方法,目前已存在许多优秀的内存泄露检测工具,除了我们常用的Valgrind外,还有mtrace、dmalloc、memwatch等优秀工具。

工具描述
valgrind一个强大开源的程序检测工具集合
mtraceGNU扩展, 用来跟踪malloc, mtrace为内存分配函数(malloc, realloc, memalign, free)安装hook函数
dmalloc用于检查C/C++内存泄露(leak)的工具,即检查是否存在直到程序运行结束还没有释放的内存,以一个运行库的方式发布
memwatch和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域
mpatrol一个跨平台的 C++ 内存泄漏检测器
dbgmem
Electric Fence

3 Valgrind工具

  Valgrind是一款基于linux平台开源的内存检测工具集合,功能强大,使用广泛。利用Valgrind工具,在检测出文章开头提及的动态内存隐患,即是Valgrind工具中的内存检测组件Memcheck, Memcheck支持的功能包括:

  • 使用空指针
  • 使用野指针
  • 内存空间访问越界
  • 内存空间未释放
  • 内存空间重复释放
  • 内存空间申请和释放不匹配

  Valgrind功能强大,是一个工具集合,除了Memcheck工具外,还提供Cachegrind、Helgrind、Callgrind、Massif工具。

  • Cachegrind

  与gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。


  • Helgrind

   Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。


  • Callgrind

  它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。


  • Massif

   堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。


  • extension

  可以根据core提供的功能,编写自定义的内存调试工具 。


  以上工具集介绍引自Linux下几款C++程序中的内存泄露检查工具文章,更新详细的介绍可以阅读原文,总结得非常好。


4 使用Valgrind工具检测内存

  Valgrind内存检测的原理是,Valgrind实现一个程序运行的虚拟环境,待检测的应用程序在Valgrind的虚拟环境中运行,Valgrind能够实时监测程序中内存申请、使用、释放的情况,并记录一些正常运行的有效信息和可能存在内存异常的信息,输出到终端或者用户指定的文件中。


4.1 Valgrind安装

  Valgrind工具实用且使用广泛,大多数主流linux系统发行版都已集成Valgrind工具,在终端输入“valgrind”,如系统没有安装Valgrind工具会提示以下安装信息。

acuity@ubuntu:~$ valgrind 
The program 'valgrind' is currently not installed. You can install it by typing:
sudo apt install valgrind

  Valgrind安装可以下载源码后安装,也通过系统源在线安装,Ubuntu推荐在线安装。

sudo apt install valgrind

  安装完成,输入“valgrind”命令,提示以下信息,表示安装成功。

acuity@ubuntu:~$ valgrind
valgrind: no program specified
valgrind: Use --help for more information.

4.2 使用说明

  • 待检测的程序编译时需加入"-g",保留调试信息
  • 检测命令格式:valgrind --tool=memcheck --leak-check=full ./file–leak-check=full表示检测所有内存泄露
  • 检测记录除了输出到终端,还可以输出到文件,这样可以监测程序长时间运行后出现异常,通过查阅文件记录分析问题
  • 更多的使用说明或者忘记命令时,可以执行valgrind -h查看帮助信息

4.3 检测程序使用空指针

例子uninit.c

#include <stdio.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

    printf("address [0x%p]\r\n", p);
	*p = 0;
	
    return 0;
}

在这里插入图片描述

  访问空指针,特别是往空指针写数据时,基本会引起段错误(Segmentation fault ),此时系统会产生core dump 文件,通过 core dump文件结合GDB工具也很容易找出访问空指针的地方。


4.4 检测程序使用野指针

例子free.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = malloc(4);
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);
	free(p);
	*p = 0;
	
    return 0;
}

在这里插入图片描述


4.5 检测程序内存空间访问越界

例子over.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = malloc(4);
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);
	p[4] = 0;
	free(p);
	
    return 0;
}

在这里插入图片描述

4.6 检测程序内存空间未释放

例子leak.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = malloc(4);
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);

    return 0;
}

在这里插入图片描述

4.7 检测程序内存空间重复释放

例子refree.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = malloc(4);
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);
	free(p);
	free(p);
	
    return 0;
}

在这里插入图片描述

4.8 检测程序内存空间申请与释放不匹配

例子nolike.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = (int*)malloc(sizeof(int));
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);
	delete p;
	
    return 0;
}

在这里插入图片描述


4.9 小结

  内存泄露检测包括动态内存使用的规范性,根本的解决办法是程序员保持良好的编码习惯,使用动态内存时谨慎考虑,保证申请与释放的必然性。因为,一些隐晦的问题可能需要在特定条件下才会引起内存泄露,依赖于检测工具也是需要长时间运行软件才能发现。

  Valgrind—memcheck工具更多是用于检测内存泄露,空指针、野指针、内存越界、重复释放等问题,会引系统段错误(Valgrind),使用GDB结合系统产生的core dump文件,也能快速定位到调用位置。而内存泄露不会立即导致系统异常,只有运行一定时间后系统申请不到内存时才会引起异常。因此,借助Valgrind—memcheck工具来检测内存泄露是一个高效的方法之一。


5 参考文章

【1】Linux下几款C++程序中的内存泄露检查工具

【2】Valgrind学习总结

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acuity.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值