admin管理员组

文章数量:1531705

目录

一、简介

二、下载安装

 三、主要工具

四、Valgrind使用

五、总结分享


一、简介

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

二、下载安装

下载地址:Valgrind: Current Releaseshttps://valgrind/downloads/

1、下载安装包

wget https://sourceware/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进行安装

make

make 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