C++ 内存泄漏检测工具——Valgrind(Linux系统)

2024-03-15 03:04

本文主要是介绍C++ 内存泄漏检测工具——Valgrind(Linux系统),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++ 内存泄漏检测工具——Valgrind(Linux系统)

参考来源:valgrind基本功能介绍、基础使用方法说明

文章目录

  • C++ 内存泄漏检测工具——Valgrind(Linux系统)
      • Valgrind
      • 下载和安装
      • Valgrind 使用选项
      • Memcheck工具
      • 例子

Valgrind

Valgrind 是Linux系统下,开放源代码的仿真调试工具的集合。我们可以利用该工具对基于C、C++语言写的项目进行内存泄漏等多个方面进行检测。

下载和安装

https://www.valgrind.org/downloads/

下载好后,到下载目录下,打开终端, 输入以下命令:

# 解压
tar -jxvf valgrind-3.21.0.tar.bz2
# 进入解压目录
cd valgrind-3.21.0
# 执行安装配置命令
./configure --prefix=/usr/local/valgrind-3.21.0
# 编译
make
# 安装
make install
# 建立软链接
ln -s /usr/local/valgrind-3.21.0/bin/valgrind /usr/bin/valgrind

好像也可以用命令安装linux valgrind 安装和使用

sudo apt-get install valgrind

Valgrind 使用选项

(1)Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
(2)Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
(3)Cachegrind。它主要用来检查程序中缓存使用出现的问题。
(4)Helgrind。它主要用来检查多线程程序中出现的竞争问题。
(5)Massif。它主要用来检查程序中堆栈使用中出现的问题。

1)适用于所有Valgrind工具
–tool=< name > 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck,还包括cachegrind callgrind helgrind drd massif dhat lackey none exp-bbv
-h --help 显示帮助信息。
–version 显示valgrind内核的版本,每个工具都有各自的版本。
-q --quiet 安静地运行,只打印错误信息。
-v --verbose 更详细的信息, 增加错误数统计。
–trace-children=no|yes 跟踪子线程? [no]
–track-fds=no|yes 跟踪打开的文件描述?[no]
–time-stamp=no|yes 增加时间戳到LOG信息? [no]
–log-fd=< number > 输出LOG到描述符文件 [2=stderr]
–log-file=< file > 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
–log-file-exactly=< file > 输出LOG信息到 file
–log-file-qualifier=< VAR > 取得环境变量的值来做为输出信息的文件名。 [none]
–log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port

(2)LOG信息输出
–xml=yes 将信息以xml格式输出,只有memcheck可用
–num-callers=< number > show < numbe r> callers in stack traces [12]
–error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]
–error-exitcode=< number > 如果发现错误则返回错误代码 [0=disable]
–db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]
–db-command=< command > 启动调试器的命令行选项[gdb -nw %f %p]

(3)适用于Memcheck工具的相关选项:
–leak-check=no|summary|full 要求对leak给出详细信息? [summary]
–leak-resolution=low|med|high how much bt merging in leak check [low]
–show-reachable=no|yes show reachable blocks in leak check? [no]
更详细的使用信息详见帮助文件、man手册或官网:http://valgrind.org/docs/manual/manual-core.html

对于内存泄漏方面:

valgrind --tool=memcheck --leak-check=full --log-file=log.txt ./main# --tool=memcheck 针对内存检测是固定使用memcheck的
# --leak-check=full 将给出内存泄漏的完整信息
# --leak-check=yes  将给出内存泄漏的信息,但是可能不太好定位
# --leak-check=summary 这个效果跟--leak-check=yes是差不多的
# --log-file=log.txt 则会将内存检测的信息写入到log.txt文件下,log.txt文件在终端当前目录下
# ./main  是linux可执行的程序,这个可执行程序名为main

Memcheck工具

Memcheck主要检测以下错误:

(1) 使用未初始化的内存(Use of uninitialised memory)
(2) 使用已经释放了的内存(Reading/writing memory after it has been free’d)
(3) 使用超过malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
(4) 对堆栈的非法访问(Reading/writing inappropriate areas on the stack)
(5) 申请的空间是否有释放(Memory leaks – where pointers to malloc’d blocks are lost forever)
(6) malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
(7) src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)

前六种是比较常见的问题,内存泄漏主要是有(4)(5)引起的,由于程序中申请了空间,但是没有在程序结束或是在函数结束时正确的释放,导致这段空间被无法在后面的程序中继续使用。对于数据量较大的程序,这是很危险的,由于内存占用问题,尽管函数或程序可能执行完毕,但是未被释放,但是内存仍被占用,这些空间无法后续使用或被其他程序使用,可能会导致计算机运行变慢,也可能导致后续程序无法正常运行。

例子

本文编译了一个名为PathPlanningFramework的程序,在程序中有一段代码是对矩阵作卷积的,为了测试是否存在内存泄漏的问题,使用如下命令。

valgrind --leak-check=full --log-file=output.txt ./PathPlanngFramework

到当面目录下查看输出log-file。

==51396== Memcheck, a memory error detector
==51396== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==51396== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==51396== Command: ./PathPlanningFramework
==51396== Parent PID: 30810
==51396== 
==51396== 
==51396== HEAP SUMMARY:
==51396==     in use at exit: 40,804 bytes in 1 blocks
==51396==   total heap usage: 1,239 allocs, 1,238 frees, 688,012 bytes allocated
==51396== 
==51396== 40,804 bytes in 1 blocks are definitely lost in loss record 1 of 1
==51396==    at 0x48487EF: malloc (vg_replace_malloc.c:442)
==51396==    by 0x1155C3: void OpMatrix::conv<float>(float*, float*, int, int, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<float, std::allocator<float> >&, int&, int&, bool, bool) (in /home/sunx/WorkSpace/Project/C++Projects/PathPlanningFramework/cmake-build-debug/PathPlanningFramework)
==51396==    by 0x116304: void OpMatrix::Conv<int, int, double>(std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >, std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >&) (in /home/sunx/WorkSpace/Project/C++Projects/PathPlanningFramework/cmake-build-debug/PathPlanningFramework)
==51396==    by 0x10F7D0: main (in /home/sunx/WorkSpace/Project/C++Projects/PathPlanningFramework/cmake-build-debug/PathPlanningFramework)
==51396== 
==51396== LEAK SUMMARY:
==51396==    definitely lost: 40,804 bytes in 1 blocks
==51396==    indirectly lost: 0 bytes in 0 blocks
==51396==      possibly lost: 0 bytes in 0 blocks
==51396==    still reachable: 0 bytes in 0 blocks
==51396==         suppressed: 0 bytes in 0 blocks
==51396== 
==51396== For lists of detected and suppressed errors, rerun with: -s
==51396== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

可以看到存在40804bytes的内存泄漏。具体的泄漏位置是在OpMatrix::conv<float>(float*, float*, int, int, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<float, std::allocator<float> >&, int&, int&, bool, bool),由这个函数引起的。这个函数被void OpMatrix::Conv<int, int, double>(std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >, std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >&),而Convmain调用,最终导致内存泄漏。

那么就去检查conv函数,看看是哪一部分的内存申请了,但是没有释放。

template <typename _Tp>void conv(_Tp* matrix, _Tp* corner, int matrix_w, int matrix_h, int corner_length, std::string conv_type, std::vector<_Tp> & result, int & result_h, int & result_w, bool show, bool is_rot) {result_w = matrix_w - corner_length + 1;result_h = matrix_h - corner_length + 1;// 卷积核逆时针旋转180度if (is_rot) {corner = rot90(corner, corner_length, 2, false);}int conv_key = convtype_str_id.at(conv_type);switch (conv_key){case ID_valid:// 卷积结果的尺寸((matrix_h - corner_length + 1),(matrix_w - corner_length + 1))result_w = matrix_w - corner_length + 1;result_h = matrix_h - corner_length + 1;result.resize(result_w*result_h);// 申请矩阵卷积的运算结果的内存空间
//                result = (_Tp*)malloc(sizeof(_Tp)*result_w*result_h);for (int i = 0; i < result_h; i++) {for (int j = 0; j < result_w; j++) {result[i*result_w + j] = 0;for (int z = 0; z < corner_length; z++) {for (int q = 0; q < corner_length; q++) {result[i*result_w + j] += matrix[(i + z)*matrix_w + j + q] * corner[z*corner_length + q];}}}}break;case ID_full://result = (_Tp*)malloc(sizeof(_Tp)*(matrix_w + corner_length - 1)*(matrix_h + corner_length - 1));// 如果corner的边长为奇数,则用corner的中心点对应matrix的扫描到的点进行卷积运算if (corner_length % 2 == 0) {std::cout << "same型卷积要求卷积核尺寸为奇数" << std::endl;return;}else {// 创建新的matrix_new,用0填充原始matrix_Tp* matrix_new = padMatrix(matrix, matrix_w, matrix_h, matrix_w + 2 * corner_length - 2, matrix_h + 2 * corner_length - 2, false);// 对matrix_new进行full卷积,变相实现same型卷积conv(matrix_new, corner, matrix_w + 2 * corner_length - 2, matrix_h + 2 * corner_length - 2, corner_length, "valid", result, result_h, result_w, show, false);// 释放新申请的空间// free(matrix_new);return;}break;case ID_same://result = (_Tp*)malloc(sizeof(_Tp)*matrix_w*matrix_h);// 如果corner的边长为奇数,则用corner的中心点对应matrix的扫描到的点进行卷积运算if (corner_length % 2 == 0) {std::cout << "same型卷积要求卷积核尺寸为奇数" << std::endl;return;}else {// 创建新的matrix_new,用0填充原始matrix_Tp* matrix_new = padMatrix(matrix, matrix_w, matrix_h, matrix_w + corner_length - 1, matrix_h + corner_length - 1, false);// 对matrix_new进行full卷积,变相实现same型卷积//free(result);conv(matrix_new, corner, matrix_w + corner_length - 1, matrix_h + corner_length - 1, corner_length, "valid", result, result_h, result_w, show, false);// 释放原始matrix// free(matrix_new);return;}break;default:throw "请输入正确的卷积操作类型!";return;}// 打印计算结果
//        if(saveR){
//            Array2Vector<_Tp1, _Tp>(save, result, result_h, result_w);
//        }if (show) {showMatrix(result, result_w, result_h);}}

仔细查看,发现是matrix_new这段空间申请了,但是没有及时释放,所以在后面合适的位置,使用free(matrix_new)释放空间即可。

==51936== Memcheck, a memory error detector
==51936== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==51936== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==51936== Command: ./PathPlanningFramework
==51936== Parent PID: 30810
==51936== 
==51936== 
==51936== HEAP SUMMARY:
==51936==     in use at exit: 0 bytes in 0 blocks
==51936==   total heap usage: 1,239 allocs, 1,239 frees, 688,012 bytes allocated
==51936== 
==51936== All heap blocks were freed -- no leaks are possible
==51936== 
==51936== For lists of detected and suppressed errors, rerun with: -s
==51936== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

修改后再次进行检测,可以看到没有内存泄漏问题。

这篇关于C++ 内存泄漏检测工具——Valgrind(Linux系统)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/810580

相关文章

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.