本文主要是介绍Valgrind调试内存泄漏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
一、简介
二、下载安装
三、主要工具
四、Valgrind使用
五、总结分享
一、简介
当你在写程序时,内存错误是常见的问题之一。有时候为了解决这种问题,你可能会耗费大量的时间和精力。今天我向大家推荐一款深得大家喜欢的工具---Valgrind,它是一个强大的工具,用于内存调试、内存泄漏检测以及性能分析,可以帮助你找到和修复这些问题,提高程序的稳定性和性能。
二、下载安装
下载地址:Valgrind: Current Releaseshttps://valgrind.org/downloads/
1、下载安装包
wget https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2
2、解压缩
tar -xjf valgrind-3.22.0.tar.bz2
3、进入到该目录下
cd valgrind-3.22.0
4、配置生成路径
./configure --prefix=`pwd`/../install
使用./configure --help 可以查看配置项。
5、利用makefile进行安装
makemake install
安装时间有点长,耐心等待一下......
三、主要工具
Valgrind提供了一些基本工具,用于不同类型的调试和性能分析。
Memcheck | 最常用的工具之一,用于检测内存错误。它可以检测到内存泄漏、未初始化的内存读取、使用已释放内存、使用无效指针等问题。Memcheck会在程序运行时对内存进行监视,并在发现问题时报告。 |
Cachegrind | 用于缓存分析(分析CPU的cache命中率、丢失率),可以帮助你了解程序的缓存访问模式,从而优化程序的性能。 |
Callgrind | 用于函数调用分析,检查程序中函数调用过程中出现的问题。 |
Helgrind | 检测多线程程序中的竞争条件和死锁。它可以帮助你发现并修复多线程程序中的同步问题。 |
接下来,向大家主要介绍Memcheck工具:
Memcheck通过在程序运行时进行内存访问的监视和跟踪来工作。它会在内存中分配一个影子(shadow)内存,用于跟踪程序中的每个字节的状态。具体来说,它会记录每个字节是否被初始化、是否被分配、是否被释放等信息。
当程序访问内存时,Memcheck会检查相应内存字节的状态,如果发现不符合规则的访问(比如读取未初始化的内存、访问已释放的内存等),就会报告错误。
Memcheck检测的主要问题:
1.非法读写错误 :
比如读写还没有分配的内存块、读写已经被free的内存块、内存访问越界。提示信息:[invalid read of size 4]
2.使用未初始化的内存/使用了未定义的值:
函数中的局部变量没有被初始化;堆上的内存块在使用之前没有被初始化。提示信息:[Conditional jump or move depends on uninitialised value]
3.系统调用中参数未已经初始化,程序提供的buffer不可用:
提示信息:[syscall param write(buf) points to uninitilaised bytes]
4.非法释放:
重复释放内存块,或者指针没有指向内存的起始位置。提示信息:[invalid free()]
5.使用不适当的释放函数释放:
-
如果与分配 malloc, calloc, realloc, valloc或者 memalign,必须使用free。
-
如果分配new,必须使用delete。
-
如果分配new[],必须使用delete[]。
提示信息:[Mismatched free()/delete/delete[]]
6.内存块重叠:
比如使用 memcpy 函数时源地址和目标地址发生重叠。提示信息:[source and destination overlap in memcpy(,)]
7.Fishy argument values:
需要的内存大小是负数或者大的离谱的正数。提示信息:[Argument ‘size’ of function malloc has a fishy (possibly negative) value: -3]
8.内存泄漏:
-
still reachable:内存指针还在还有机会使用或释放,指针指向的动态内存还没有被释放就退出了
-
definitely lost:确定的内存泄露,已经不能访问这块内存
-
indirectly lost:指向该内存的指针都位于内存泄露处
-
possibly lost:可能的内存泄露,仍然存在某个指针能够快速访问某块内存,但该指针指向的已经不是内存首位置
四、Valgrind使用
一共两步:
1、编译
gcc test.c -g -o test
2、使用Memcheck进行检测
valgrind --tool=memcheck --leak-check=yes --log-file=check.log ./test
-tool=<name> 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。
-log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID-leak-check=no|summary|full 要求对leak给出详细信息? [summary]
接下来,叫我们用几个代码来实际操作一下吧!
(1)使用未初始化的内存(野指针)
#include <stdio.h>int main()
{char *p; char c = *p; printf("%c\n",c);return 0;
}
(2)在内存被释放后进行读/写(使用野指针)
#include <stdio.h>
#include <stdlib.h>int main()
{int *p = (int *)malloc(sizeof(int));*p = 100;free(p);int a = *p; return 0;
}
(3)内存泄漏
#include <stdio.h>
#include <stdlib.h>int main()
{int *p = (int *)malloc(sizeof(int)*100);*p = 100;return 0;
}
堆内存泄露报告
-
堆内存使用情况概述(HEAP SUMMARY)
-
确信的内存泄露报告(definitely lost)
-
可疑内存操作报告 (show-reachable=no关闭,打开:–show-reachable=yes)
-
泄露情况概述(LEAK SUMMARY)
五、总结分享
当你在写程序时,你可能会感觉到疑惑,明明我的程序没有调用malloc,为什么Valgrind显示我多次调用malloc引起内存泄漏?
这可能是因为你的代码中使用了依赖库或框架,在其内部可能使用了malloc或类似的动态内存分配函数。Valgrind 会跟踪整个程序的内存分配和释放情况,因此它会显示出这些依赖库或框架中的动态内存分配情况,即使你的代码本身没有直接使用malloc等函数。
接下来和大家分享一下我在程序中遇见的内存泄漏问题。
1、sqlite3_get_table()函数
该函数原型如下:
int sqlite3_get_table(sqlite3 *db, /* Database handle */const char *Sql, /* SQL query to be executed */char ***Result, /* Pointer to array of result rows */int *nRow, /* Number of result rows written here */int *nColumn, /* Number of result columns written here */char **Errmsg /* Error msg written here */
);
这个函数的工作原理是执行 SQL 查询并将结果存储在一个二维字符数组中,然后返回指向这个数组的指针。因此,它在内部会使用malloc来分配足够的内存来容纳查询结果。
由于sqlite3_get_table使用了malloc,所以在使用完查询结果后,必须调用sqlite3_free_table 来释放由sqlite3_get_table 分配的内存,以避免内存泄漏问题。这个函数会释放整个结果数组及其内部的字符串数组所分配的内存。
除此之外,如果在调用 sqlite3_get_table函数时出现了错误(比如语法错误、查询失败等),则 Errmsg
指向的内存将被写入错误信息。在处理完错误后,调用者需要调用sqlite3_free释放Errmsg
指向的内存,以避免内存泄漏。
2、getaddrinfo()函数
该函数原型如下:
int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints, struct addrinfo **res);
函数是用于执行主机名到 IP 地址以及服务名到端口号的转换的函数,它在 C 语言的网络编程中非常常用。该函数在内部可能会调用malloc函数,用于动态分配内存来存储返回的结果结构体链表。
具体来说,getaddrinfo
函数会返回一个 addrinfo
结构体链表,每个节点表示一个可能的主机名和服务名对应的 IP 地址和端口号。这些 addrinfo
结构体的内存是在 getaddrinfo
函数内部使用 malloc
动态分配的。因此,在使用 getaddrinfo
函数时,需要注意在使用完返回的结果后,调用 freeaddrinfo
函数来释放动态分配的内存,避免内存泄漏。
目前就遇到这么多问题,欢迎大家补充,如果后续遇到更多问题,我会再进行详细补充!
这篇关于Valgrind调试内存泄漏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!