Valgrind调试内存泄漏

2024-04-19 19:36
文章标签 内存 调试 泄漏 valgrind

本文主要是介绍Valgrind调试内存泄漏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、简介

二、下载安装

 三、主要工具

四、Valgrind使用

五、总结分享


一、简介

        当你在写程序时,内存错误是常见的问题之一。有时候为了解决这种问题,你可能会耗费大量的时间和精力。今天我向大家推荐一款深得大家喜欢的工具---Valgrind,它是一个强大的工具,用于内存调试、内存泄漏检测以及性能分析,可以帮助你找到和修复这些问题,提高程序的稳定性和性能。

二、下载安装

下载地址:Valgrind: Current Releasesicon-default.png?t=N7T8https://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调试内存泄漏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/918368

相关文章

使用Python自建轻量级的HTTP调试工具

《使用Python自建轻量级的HTTP调试工具》这篇文章主要为大家详细介绍了如何使用Python自建一个轻量级的HTTP调试工具,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录一、为什么需要自建工具二、核心功能设计三、技术选型四、分步实现五、进阶优化技巧六、使用示例七、性能对比八、扩展方向建

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

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

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在