当前位置: 首页 > news >正文

利用 Valgrind 检测 C++ 内存泄露

Valgrind 是一款运行在 Linux 系统上的编程工具集,主要用于调试和分析程序的性能、内存使用等问题。其中最常用的工具是 Memcheck,它可以帮助检测 C 和 C++ 程序中的内存管理错误,如内存泄漏、使用未初始化的内存、越界访问等。

安装

这里我以Ubuntu 22.04(WSL)为例子,安装很简单:

sudo apt-get install valgrind
bluebonnet27@bluebonnet27:~/Project/CPP$ valgrind --version
valgrind-3.18.1

使用

编写包含潜在内存泄露问题的 C++ 代码

以下是一个简单的 C++ 程序示例,其中存在内存泄露问题

#include <iostream>int main() {int* ptr = new int[10];// 没有释放分配的内存return 0;
}

在这个程序中,我们使用new操作符分配了一个包含 10 个整数的数组,但没有使用delete[]来释放这块内存,从而导致了内存泄露。

编译程序

使用g++编译器编译程序,并确保开启调试信息(使用-g选项),这样 Valgrind 在检测到问题时可以提供更详细的错误信息:

g++ -g -o memLeak memLeak.cpp

使用 Valgrind 运行程序

使用 Valgrind 的 Memcheck 工具来运行编译好的程序:

valgrind --leak-check=full --show-leak-kinds=all ./memLeak

分析 Valgrind 的输出结果

运行上述命令后,Valgrind 会输出详细的内存使用信息和检测到的问题

==36044== Memcheck, a memory error detector
==36044== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==36044== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==36044== Command: ./memLeak
==36044== 
==36044== 
==36044== HEAP SUMMARY:
==36044==     in use at exit: 40 bytes in 1 blocks
==36044==   total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated
==36044== 
==36044== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==36044==    at 0x484A2F3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==36044==    by 0x10919E: main (memLeak.cpp:4)
==36044== 
==36044== LEAK SUMMARY:
==36044==    definitely lost: 40 bytes in 1 blocks
==36044==    indirectly lost: 0 bytes in 0 blocks
==36044==      possibly lost: 0 bytes in 0 blocks
==36044==    still reachable: 0 bytes in 0 blocks
==36044==         suppressed: 0 bytes in 0 blocks
==36044== 
==36044== For lists of detected and suppressed errors, rerun with: -s
==36044== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

从输出中可以看到,definitely lost: 40 bytes in 1 blocks 表示确定存在 40 字节的内存泄露,并且指出泄露发生在 memLeak.cpp 文件的第 4 行,这正是我们使用new分配内存但没有释放的地方。

修复内存泄露问题

根据 Valgrind 的输出信息,修改代码来释放分配的内存:

#include <iostream>int main() {int* ptr = new int[10];// 释放分配的内存delete[] ptr;return 0;
}

重新编译并使用 Valgrind 运行修改后的程序

==36689== Memcheck, a memory error detector
==36689== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==36689== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==36689== Command: ./memLeak
==36689== 
==36689== 
==36689== HEAP SUMMARY:
==36689==     in use at exit: 0 bytes in 0 blocks
==36689==   total heap usage: 2 allocs, 2 frees, 72,744 bytes allocated
==36689== 
==36689== All heap blocks were freed -- no leaks are possible
==36689== 
==36689== For lists of detected and suppressed errors, rerun with: -s
==36689== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

此时不再有内存泄漏。

valgrind的局限性

下面的代码也存在内存泄漏,因为使用基类指针指向派生类对象,并且基类的析构函数不是虚函数时,通过基类指针删除对象可能不会调用派生类的析构函数,从而导致派生类部分的内存没有被正确释放。

#include <iostream>class Base {
public:Base() { std::cout << "Base constructor" << std::endl; }~Base() { std::cout << "Base destructor" << std::endl; }
};class Derived : public Base {
public:Derived() { std::cout << "Derived constructor" << std::endl; }~Derived() { std::cout << "Derived destructor" << std::endl; }
};void memoryLeak4() {Base* ptr = new Derived();delete ptr; // 只会调用基类的析构函数,派生类部分内存未释放
}int main() {memoryLeak4();return 0;
}

但这样的代码并不能被valgrind检测出内存泄漏,因为 Valgrind 主要基于内存分配和释放操作来判断是否有泄漏,而这种情况下内存的释放操作确实执行了(基类部分的内存被释放),只是派生类部分的析构函数没有被调用,没有从表面上体现出 “未释放” 的特征。

我们修改下代码,让Derived 类的构造函数里使用 new[] 分配了一个包含 10 个 int 元素的数组,而析构函数中使用 delete[] 来释放这块内存。当使用基类指针 Base* ptr = new Derived(); 创建派生类对象,并且基类析构函数不是虚函数时,执行 delete ptr; 只会调用基类的析构函数,派生类的析构函数不会被调用,这就导致 data 所指向的内存没有被释放。

#include <iostream>class Base {
public:Base() { std::cout << "Base constructor" << std::endl; }~Base() { std::cout << "Base destructor" << std::endl; }
};class Derived : public Base {
private:int* data;
public:Derived() {std::cout << "Derived constructor" << std::endl;data = new int[10]; // 派生类中动态分配内存}~Derived() {std::cout << "Derived destructor" << std::endl;delete[] data; // 释放派生类中分配的内存}
};void memoryLeak4() {Base* ptr = new Derived();delete ptr; // 只会调用基类的析构函数,派生类部分内存未释放
}int main() {memoryLeak4();return 0;
}

此时 Valgrind 就能检测到内存泄漏了,并正确打印了内存泄漏的代码行:

==37900== Memcheck, a memory error detector
==37900== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==37900== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==37900== Command: ./memLeak
==37900== 
Base constructor
Derived constructor
Base destructor
==37900== 
==37900== HEAP SUMMARY:
==37900==     in use at exit: 40 bytes in 1 blocks
==37900==   total heap usage: 4 allocs, 3 frees, 73,776 bytes allocated
==37900== 
==37900== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==37900==    at 0x484A2F3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==37900==    by 0x1093EB: Derived::Derived() (memLeak.cpp:15)
==37900==    by 0x10924C: memoryLeak4() (memLeak.cpp:24)
==37900==    by 0x1092A6: main (memLeak.cpp:29)
==37900== 
==37900== LEAK SUMMARY:
==37900==    definitely lost: 40 bytes in 1 blocks
==37900==    indirectly lost: 0 bytes in 0 blocks
==37900==      possibly lost: 0 bytes in 0 blocks
==37900==    still reachable: 0 bytes in 0 blocks
==37900==         suppressed: 0 bytes in 0 blocks
==37900== 
==37900== For lists of detected and suppressed errors, rerun with: -s
==37900== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
http://www.lryc.cn/news/538018.html

相关文章:

  • Python中的HTTP客户端库:httpx与request | python小知识
  • 【Python】Python入门基础——环境搭建
  • 2025 pwn_A_childs_dream
  • 面试题整理:操作系统
  • 构建未来教育的基石:智慧校园与信息的重要性
  • C# 控制台相关 API 与随机数API
  • 【踩坑】⭐️MyBatis的Mapper接口中不建议使用重载方法
  • CSS Grid 网格布局,以及 Flexbox 弹性盒布局模型,它们的适用场景是什么?
  • HDFS体系结构
  • AI大模型的技术突破与传媒行业变革
  • vscode/cursor+godot C#中使用socketIO
  • 分段线性插值
  • 制作一个项目用于研究elementUI的源码
  • [AI]从零开始的llama.cpp部署与DeepSeek格式转换、量化、运行教程
  • vLLM专题(二):安装-CPU
  • JVM 底层探秘:对象创建的详细流程、内存分配机制解析以及线程安全保障策略
  • 【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter14-DOM
  • 外汇掉期(FX Swap):全球企业管理外汇风险的关键工具(中英双语)
  • verilog程序设计及SystemVerilog验证
  • Unity DeepSeek API 聊天接入教程(0基础教学)
  • 力扣 乘积最大子数组
  • ABP - 事件总线之分布式事件总线
  • osgearth控件显示中文(八)
  • 基于opencv的 24色卡IQA评测算法源码-可完全替代Imatest
  • webpack打包优化策略
  • Kafka日志数据深度解析:从基础查看到高级操作全攻略
  • DeepSeek-R1使用生存指南
  • Code::Blocks 创建 C 项目 二
  • pyqt写一个待办程序
  • 总结前端常用数据结构 之 数组篇【JavaScript -包含常用数组方法】