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++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象