深圳建网站公司哪家好百度推广营销页
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)