如何使用valgrind查找内存泄漏?

use*_*457 134 c valgrind

如何使用valgrind查找程序中的内存泄漏?

请有人帮助我并描述执行程序的步骤?

我正在使用Ubuntu 10.04,我有一个程序a.c,请帮帮我.

Jos*_*ler 202

如何运行Valgrind

我想为如何有效地使用Valgrind以及如何解决内存泄漏建立一个更详细的解释.不要侮辱OP,但对于那些提出这个问题并且仍然是Linux新手的人 - 你可能必须在你的系统上安装Valgrind.

sudo apt install valgrind  # Ubuntu, Debian, etc.
sudo yum install valgrind  # RHEL, CentOS, Fedora, etc.
Run Code Online (Sandbox Code Playgroud)

Valgrind可以很容易地用于C/C++代码,但是在正确配置时甚至可以用于其他语言(参见Python的这个).

要运行valgrind,请将可执行文件作为参数传递(以及任何参数到程序中).

valgrind --leak-check=full \
         --show-leak-kinds=all \
         --track-origins=yes \
         --verbose \
         --log-file=valgrind-out.txt \
         ./executable exampleParam1
Run Code Online (Sandbox Code Playgroud)

这将在执行程序结束时生成一个报告(希望如下):

HEAP SUMMARY:
    in use at exit: 0 bytes in 0 blocks
  total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated

All heap blocks were freed -- no leaks are possible

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Run Code Online (Sandbox Code Playgroud)

我有泄漏,但在哪里

所以,你有内存泄漏,Valgrind并没有说任何有意义的事情.也许,这样的事情:

5 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x40053E: main (in /home/Peri461/Documents/executable)
Run Code Online (Sandbox Code Playgroud)

我们来看看我写的C代码:

#include <stdlib.h>

int main() {
    char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

好吧,丢失了5个字节.这是怎么发生的?错误报告只是说 mainmalloc.在一个更大的计划中,追捕会非常麻烦.这是因为可执行文件的编译方式.我们实际上可以获得有关出错的逐行详细信息.用调试标志重新编译你的程序(我在gcc这里使用):

gcc -o executable -std=c11 -Wall main.c         # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c  # add -ggdb3 to it
Run Code Online (Sandbox Code Playgroud)

现在有了这个调试版本,Valgrind指向 分配泄漏内存的确切代码行!(措辞很重要:它可能不是你泄漏的确切位置,但泄漏的是什么.痕迹可以帮助你找到 位置.)

5 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x40053E: main (main.c:4)
Run Code Online (Sandbox Code Playgroud)

调试内存泄漏和错误的技术

  • 利用www.cplusplus.com!它有很好的C/C++函数文档.
  • 内存泄漏的一般建议:
    • 确保动态分配的内存实际上已释放.
    • 不要分配内存而忘记分配指针.
    • 除非释放旧内存,否则不要使用新指针覆盖指针.
  • 内存错误的一般建议:
    • 访问并写入您确定属于您的地址和索引.内存错误与泄漏不同; 他们经常只是IndexOutOfBoundsException 类型问题.
    • 释放后不要访问或写入内存.
  • 有时Valgrind并不总是准确地指出您的错误所在.有时你会遇到很多错误并且解决其中一个错误会解决其他问题.
    • 列出代码中依赖于/依赖于具有内存错误的"违规"代码的函数.遵循程序的执行(可能甚至gdb可能),并查找前置条件/​​后置条件错误.
    • 尝试评论"违规"代码块(在合理范围内,因此您的代码仍然可以编译).如果Valgrind错误消失了,你就找到了它的位置.
  • 如果所有其他方法都失败了,请尝试查找.Valgrind也有文档!

看看常见的泄漏和错误

看你的指针

60 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
   by 0x4005E4: resizeArray (main.c:12)
   by 0x40062E: main (main.c:19)
Run Code Online (Sandbox Code Playgroud)

和代码:

#include <stdlib.h>
#include <stdint.h>

struct _List {
    int32_t* data;
    int32_t length;
};
typedef struct _List List;

List* resizeArray(List* array) {
    int32_t* dPtr = array->data;
    dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
    return array;
}

int main() {
    List* array = calloc(1, sizeof(List));
    array->data = calloc(10, sizeof(int32_t));
    array = resizeArray(array);

    free(array->data);
    free(array);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

作为助教,我经常看到这个错误.学生使用局部变量并忘记更新原始指针.这里的错误是注意到realloc实际上可以将分配的内存移动到其他地方并更改指针的位置.然后我们离开resizeArray而没有告诉 array->data阵列被移动到哪里.

写入无效

1 errors in context 1 of 1:
Invalid write of size 1
   at 0x4005CA: main (main.c:10)
 Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
   at 0x4C2B975: calloc (vg_replace_malloc.c:711)
   by 0x400593: main (main.c:5)
Run Code Online (Sandbox Code Playgroud)

和代码:

#include <stdlib.h>
#include <stdint.h>

int main() {
    char* alphabet = calloc(26, sizeof(char));

    for(uint8_t i = 0; i < 26; i++) {
        *(alphabet + i) = 'A' + i;
    }
    *(alphabet + 26) = '\0'; //null-terminate the string?

    free(alphabet);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,Valgrind将我们指向上面的注释代码行.大小为26的数组被索引[0,25],这就是为什么*(alphabet + 26)无效的写入超出界限的原因.无效写入是逐个错误的常见结果.查看作业操作的左侧.

阅读无效

1 errors in context 1 of 1:
Invalid read of size 1
   at 0x400602: main (main.c:9)
 Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x4005E1: main (main.c:6)
Run Code Online (Sandbox Code Playgroud)

和代码:

#include <stdlib.h>
#include <stdint.h>

int main() {
    char* destination = calloc(27, sizeof(char));
    char* source = malloc(26 * sizeof(char));

    for(uint8_t i = 0; i < 27; i++) {
        *(destination + i) = *(source + i); //Look at the last iteration.
    }

    free(destination);
    free(source);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Valgrind将我们指向上面的注释行.看看这里的最后一次迭代,即
*(destination + 26) = *(source + 26);.但是,*(source + 26)再次出界,类似于无效写入.无效读取也是逐个错误的常见结果.查看作业操作的右侧.


开源(U/Dys)topia

我如何知道泄漏何时是我的?当我使用别人的代码时,如何找到泄密点?我发现了一个不是我的泄漏; 我该怎么办?一切都是合法的问题.首先,2个真实世界的例子,显示了两类常见的遭遇.

Jansson:一个JSON库

#include <jansson.h>
#include <stdio.h>

int main() {
    char* string = "{ \"key\": \"value\" }";

    json_error_t error;
    json_t* root = json_loads(string, 0, &error); //obtaining a pointer
    json_t* value = json_object_get(root, "key"); //obtaining a pointer
    printf("\"%s\" is the value field.\n", json_string_value(value)); //use value

    json_decref(value); //Do I free this pointer?
    json_decref(root);  //What about this one? Does the order matter?
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的程序:它读取JSON字符串并解析它.在制作过程中,我们使用库调用来为我们进行解析.Jansson动态地进行必要的分配,因为JSON可以包含自身的嵌套结构.然而,这并不意味着我们decref或"释放"从每个功能给予我们的记忆.事实上,我上面写的这段代码抛出了"无效读取"和"无效写入".当你拿出decref线路时,这些错误就会消失value.

为什么?该变量value被视为Jansson API中的"借用参考".Jansson为您记录其内存,您只需要decref 相互独立的JSON结构.这里的教训: 阅读文档.真.它有时很难理解,但它们告诉你为什么会发生这些事情.相反,我们 对此内存错误存在疑问.

SDL:图形和游戏库

#include "SDL2/SDL.h"

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
        SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
        return 1;
    }

    SDL_Quit();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这段代码出了什么问题?它一直为我泄漏~212 KiB的内存.花点时间考虑一下.我们打开SDL然后关闭.回答?没有任何错误.

起初这可能听起来很怪异.说实话,图形很乱,有时你必须接受一些泄漏作为标准库的一部分.这里有一课:你不需要平息每一次内存泄漏.有时您只需要压制泄漏, 因为它们是已知的问题,您无法做任何事情.(这不是我允许忽略你自己的泄漏!)

回答虚空

我如何知道泄漏何时是我的?
它是.(99%肯定,无论如何)

当我使用别人的代码时,如何找到泄密点?
有可能是其他人已经找到了它.试试谷歌!如果失败了,请使用我上面给你的技能.如果失败并且您主要看到API调用并且几乎没有自己的堆栈跟踪,请参阅下一个问题.

我发现了一个不是我的泄漏; 我该怎么办?
是! 大多数API都有报告错误和问题的方法.使用它们!帮助回复您在项目中使用的工具!


进一步阅读

谢谢你和我在一起这么久.我希望你已经学到了一些东西,因为我试图倾向于广泛的人们得出这个答案.我希望你在路上问过一些事情:C的内存分配器是如何工作的?实际上是内存泄漏和内存错误?它们与段错误有什么不同?Valgrind如何运作?如果您有任何这些,请满足您的好奇心:

  • @ A.Smoliak这是因为我在OP的问题后写了6年的答案.我写这篇文章是因为这是成千上万程序员的登陆页面,关于"如何找到内存泄漏"这个问题的部分内容没有得体.他们说,运行Valgrind.他们说,了解输出是作为编码员的练习.不,我可能不是Valgrind的专家,因为我只是一名本科生,但Stack Overflow必须提供有意义的答案.我很高兴我的回答帮助人们发现他们的记忆泄漏. (8认同)
  • 我可以为这个答案加注星标并将其用作我自己的未来参考吗?干得好! (4认同)
  • 更好的答案,遗憾的是这不是公认的答案. (2认同)

Rag*_*geD 139

试试这个:

valgrind --leak-check=full -v ./your_program

只要安装了valgrind,它就会通过你的程序告诉你出了什么问题.它可以为您提供指针和可能发现泄漏的大致位置.如果您是段错误,请尝试运行它gdb.

  • `your_program` ==可执行文件名或用于运行应用程序的任何命令. (6认同)

小智 25

你可以运行:

valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
Run Code Online (Sandbox Code Playgroud)


小智 10

您可以在 .bashrc 文件中创建别名,如下所示

alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'
Run Code Online (Sandbox Code Playgroud)

所以每当你想检查内存泄漏时,只需简单地做

vg ./<name of your executable> <command line parameters to your executable>
Run Code Online (Sandbox Code Playgroud)

这将在当前目录中生成一个 Valgrind 日志文件。