我是Windows平台上的c++程序员。我使用的是Visual Studio 2008。

我通常会在代码中出现内存泄漏。

通常我通过检查代码来发现内存泄漏,但这很麻烦,而且并不总是一种好方法。

因为我买不起付费的内存泄漏检测工具,所以我想让你们建议避免内存泄漏的最佳方法。

我想知道如何程序员可以找到内存泄漏。 是否应该遵循某种标准或过程来确保程序中没有内存泄漏?


当前回答

在visual studio中,有一个内置的内存泄漏检测器,称为C运行时库。当主函数返回后程序退出时,CRT将检查应用程序的调试堆。如果在调试堆上仍然分配了任何块,那么就存在内存泄漏。 本论坛讨论了在C/ c++中避免内存泄漏的一些方法。

其他回答

自动内存泄漏检查器的概述

在这个回答中,我在一个简单易懂的内存泄漏示例中比较了几种不同的内存泄漏检查程序。

在做任何事情之前,请参阅ASan wiki中比较人类已知所有工具的巨大表格:https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

分析的例子如下:

c

#include <stdlib.h>

void * my_malloc(size_t n) {
    return malloc(n);
}

void leaky(size_t n, int do_leak) {
    void *p = my_malloc(n);
    if (!do_leak) {
        free(p);
    }
}

int main(void) {
    leaky(0x10, 0);
    leaky(0x10, 1);
    leaky(0x100, 0);
    leaky(0x100, 1);
    leaky(0x1000, 0);
    leaky(0x1000, 1);
}

GitHub上游。

我们将尝试看看不同的工具如何清楚地向我们指出泄漏的调用。

从gperftools通过谷歌

https://github.com/gperftools/gperftools

Ubuntu 19.04的用法:

sudo apt-get install google-perftools
gcc -ggdb3 -o main.out main.c -ltcmalloc
PPROF_PATH=/usr/bin/google-pprof \
  HEAPCHECK=normal \
  HEAPPROFILE=ble \
  ./main.out \
;
google-pprof main.out ble.0001.heap --text

程序运行的输出包含内存泄漏分析:

WARNING: Perftools heap leak checker is active -- Performance may suffer
Starting tracking the heap
Dumping heap profile to ble.0001.heap (Exiting, 4 kB in use)
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 272 bytes in 2 objects
The 2 largest leaks:
Using local file ./main.out.
Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start


If the preceding stack traces are not enough to find the leaks, try running THIS shell command:

pprof ./main.out "/tmp/main.out.24744._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv

If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more re
Exiting with error code (instead of crashing) because of whole-program memory leaks

google-pprof的输出包含堆使用分析:

Using local file main.out.
Using local file ble.0001.heap.
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% my_malloc
     0.0   0.0% 100.0%      0.0 100.0% __libc_start_main
     0.0   0.0% 100.0%      0.0 100.0% _start
     0.0   0.0% 100.0%      0.0 100.0% leaky
     0.0   0.0% 100.0%      0.0 100.0% main

输出为我们指出了三个泄漏中的两个:

Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start

我不知道为什么第三个没出现

在任何情况下,通常当某些东西泄漏时,它会发生很多次,当我在一个真实的项目中使用它时,我只是很容易就被指出泄漏函数。

正如输出本身所提到的,这会导致显著的执行速度放缓。

进一步文件载于:

https://gperftools.github.io/gperftools/heap_checker.html https://gperftools.github.io/gperftools/heapprofile.html

参见:如何使用TCMalloc?

在Ubuntu 19.04中测试,google-perftools 2.5-2。

地址消毒(ASan)也由谷歌

https://github.com/google/sanitizers

前面提到过:如何在c++代码/项目中找到内存泄漏?TODO vs tcmalloc。

这已经集成到GCC中,所以你可以这样做:

gcc -fsanitize=address -ggdb3 -o main.out main.c
./main.out 

和执行输出:

=================================================================
==27223==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f210 in main /home/ciro/test/main.c:20
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 256 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1f2 in main /home/ciro/test/main.c:18
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1d4 in main /home/ciro/test/main.c:16
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

SUMMARY: AddressSanitizer: 4368 byte(s) leaked in 3 allocation(s).

它清楚地识别所有泄漏。好了!

ASan还可以做其他很酷的检查,比如越界写入:检测到堆栈破坏

在Ubuntu 19.04, GCC 8.3.0中测试。

Valgrind

http://www.valgrind.org/

之前提到过:https://stackoverflow.com/a/37661630/895245

用法:

sudo apt-get install valgrind
gcc -ggdb3 -o main.out main.c
valgrind --leak-check=yes ./main.out

输出:

==32178== Memcheck, a memory error detector
==32178== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32178== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32178== Command: ./main.out
==32178== 
==32178== 
==32178== HEAP SUMMARY:
==32178==     in use at exit: 4,368 bytes in 3 blocks
==32178==   total heap usage: 6 allocs, 3 frees, 8,736 bytes allocated
==32178== 
==32178== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091B4: main (main.c:16)
==32178== 
==32178== 256 bytes in 1 blocks are definitely lost in loss record 2 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091D2: main (main.c:18)
==32178== 
==32178== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091F0: main (main.c:20)
==32178== 
==32178== LEAK SUMMARY:
==32178==    definitely lost: 4,368 bytes in 3 blocks
==32178==    indirectly lost: 0 bytes in 0 blocks
==32178==      possibly lost: 0 bytes in 0 blocks
==32178==    still reachable: 0 bytes in 0 blocks
==32178==         suppressed: 0 bytes in 0 blocks
==32178== 
==32178== For counts of detected and suppressed errors, rerun with: -v
==32178== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

所以再一次,所有的泄漏都被检测到了。

请参见:如何使用valgrind查找内存泄漏?

在Ubuntu 19.04, valgrind 3.14.0中测试。

您可以在代码中使用一些技术来检测内存泄漏。最常见和最简单的检测方法是,定义一个宏(比如DEBUG_NEW),并使用它与预定义的宏(如__FILE__和__LINE__)一起定位代码中的内存泄漏。这些预定义的宏告诉您内存泄漏的文件号和行号。

DEBUG_NEW只是一个宏,通常定义为:

#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW

因此,无论在哪里使用new,它都可以跟踪文件和行号,这可以用来定位程序中的内存泄漏。

而__FILE__, __LINE__是预定义的宏,它们分别计算你使用它们的文件名和行号!

阅读下面的文章,它解释了使用DEBUG_NEW和其他有趣的宏的技术,非常漂亮:

一个跨平台的内存泄漏检测器


从Wikpedia,

Debug_new refers to a technique in C++ to overload and/or redefine operator new and operator delete in order to intercept the memory allocation and deallocation calls, and thus debug a program for memory usage. It often involves defining a macro named DEBUG_NEW, and makes new become something like new(_FILE_, _LINE_) to record the file/line information on allocation. Microsoft Visual C++ uses this technique in its Microsoft Foundation Classes. There are some ways to extend this method to avoid using macro redefinition while still able to display the file/line information on some platforms. There are many inherent limitations to this method. It applies only to C++, and cannot catch memory leaks by C functions like malloc. However, it can be very simple to use and also very fast, when compared to some more complete memory debugger solutions.

确保成功释放了所有堆内存。如果您从未在堆上分配内存,则不需要这样做。如果你这样做,计算你malloc内存的次数,并计算你释放内存的次数。

在应用程序代码中不应该使用“new”或“delete”。相反,应该创建一个使用管理器/工作人员习惯用法的新类型,其中管理器类分配和释放内存,并将所有其他操作转发给工作人员对象。

不幸的是,这比它应该做的要多,因为c++没有“operator .”的重载。如果存在多态,则工作量更大。

但是这样做是值得的,因为这样您就不必担心内存泄漏,这意味着您甚至不需要寻找它们。

Download Debugging Tools for Windows. Use the gflags utility to turn on user-mode stack traces. Use UMDH to take multiple snapshots of your program's memory. Take a snapshot before memory gets allocated, and take a second snapshot after a point at which you believe that your program has leaked memory. You might want to add pauses or prompts in your program to give you a chance to run UMDH and take the snapshots. Run UMDH again, this time in its mode that does a diff between the two snapshots. It will then generate a report containing the call stacks of suspected memory leaks. Restore your previous gflags settings when you're done.

UMDH会给你比CRT调试堆更多的信息,因为它会监视整个进程的内存分配;它甚至可以告诉您第三方组件是否泄漏。