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

相关文章

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注